Merge "Merge profiles without needing to creating profile_compilation_info object"
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 115e722..6ee9cc6 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -2593,10 +2593,6 @@
CopyReference(copy->GetDeclaringClassAddressWithoutBarrier(), orig->GetDeclaringClassUnchecked());
- mirror::MethodDexCacheType* orig_resolved_methods =
- orig->GetDexCacheResolvedMethods(target_ptr_size_);
- copy->SetDexCacheResolvedMethods(NativeLocationInImage(orig_resolved_methods), target_ptr_size_);
-
// OatWriter replaces the code_ with an offset value. Here we re-adjust to a pointer relative to
// oat_begin_
diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc
index 18ff1c9..4ca5afe 100644
--- a/compiler/linker/arm/relative_patcher_arm_base.cc
+++ b/compiler/linker/arm/relative_patcher_arm_base.cc
@@ -28,7 +28,7 @@
class ArmBaseRelativePatcher::ThunkData {
public:
ThunkData(std::vector<uint8_t> code, uint32_t max_next_offset)
- : code_(code),
+ : code_(std::move(code)),
offsets_(),
max_next_offset_(max_next_offset),
pending_offset_(0u) {
diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64.cc
index 9095ecd..18a55c8 100644
--- a/compiler/optimizing/code_generator_vector_arm64.cc
+++ b/compiler/optimizing/code_generator_vector_arm64.cc
@@ -27,12 +27,13 @@
using helpers::ARM64EncodableConstantOrRegister;
using helpers::Arm64CanEncodeConstantAsImmediate;
using helpers::DRegisterFrom;
-using helpers::VRegisterFrom;
using helpers::HeapOperand;
using helpers::InputRegisterAt;
using helpers::Int64ConstantFrom;
-using helpers::XRegisterFrom;
+using helpers::OutputRegister;
+using helpers::VRegisterFrom;
using helpers::WRegisterFrom;
+using helpers::XRegisterFrom;
#define __ GetVIXLAssembler()->
@@ -127,20 +128,51 @@
}
}
-void LocationsBuilderARM64::VisitVecSetScalars(HVecSetScalars* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+void LocationsBuilderARM64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimLong:
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresRegister());
+ break;
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
-void InstructionCodeGeneratorARM64::VisitVecSetScalars(HVecSetScalars* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARM64::VisitVecSumReduce(HVecSumReduce* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorARM64::VisitVecSumReduce(HVecSumReduce* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+void InstructionCodeGeneratorARM64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ VRegister src = VRegisterFrom(locations->InAt(0));
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimInt:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ Umov(OutputRegister(instruction), src.V4S(), 0);
+ break;
+ case Primitive::kPrimLong:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ Umov(OutputRegister(instruction), src.V2D(), 0);
+ break;
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ DCHECK_LE(2u, instruction->GetVectorLength());
+ DCHECK_LE(instruction->GetVectorLength(), 4u);
+ DCHECK(locations->InAt(0).Equals(locations->Out())); // no code required
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
// Helper to set up locations for vector unary operations.
@@ -169,6 +201,46 @@
}
}
+void LocationsBuilderARM64::VisitVecReduce(HVecReduce* instruction) {
+ CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitVecReduce(HVecReduce* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ VRegister src = VRegisterFrom(locations->InAt(0));
+ VRegister dst = DRegisterFrom(locations->Out());
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimInt:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ switch (instruction->GetKind()) {
+ case HVecReduce::kSum:
+ __ Addv(dst.S(), src.V4S());
+ break;
+ case HVecReduce::kMin:
+ __ Sminv(dst.S(), src.V4S());
+ break;
+ case HVecReduce::kMax:
+ __ Smaxv(dst.S(), src.V4S());
+ break;
+ }
+ break;
+ case Primitive::kPrimLong:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ switch (instruction->GetKind()) {
+ case HVecReduce::kSum:
+ __ Addp(dst.D(), src.V2D());
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD min/max";
+ UNREACHABLE();
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
+}
+
void LocationsBuilderARM64::VisitVecCnv(HVecCnv* instruction) {
CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
}
@@ -263,6 +335,7 @@
break;
default:
LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
}
}
@@ -805,6 +878,77 @@
}
}
+void LocationsBuilderARM64::VisitVecSetScalars(HVecSetScalars* instruction) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+
+ DCHECK_EQ(1u, instruction->InputCount()); // only one input currently implemented
+
+ HInstruction* input = instruction->InputAt(0);
+ bool is_zero = IsZeroBitPattern(input);
+
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimLong:
+ locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+ : Location::RequiresRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ break;
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+ : Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
+}
+
+void InstructionCodeGeneratorARM64::VisitVecSetScalars(HVecSetScalars* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ VRegister dst = VRegisterFrom(locations->Out());
+
+ DCHECK_EQ(1u, instruction->InputCount()); // only one input currently implemented
+
+ // Zero out all other elements first.
+ __ Movi(dst.V16B(), 0);
+
+ // Shorthand for any type of zero.
+ if (IsZeroBitPattern(instruction->InputAt(0))) {
+ return;
+ }
+
+ // Set required elements.
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ DCHECK_EQ(16u, instruction->GetVectorLength());
+ __ Mov(dst.V16B(), 0, InputRegisterAt(instruction, 0));
+ break;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ __ Mov(dst.V8H(), 0, InputRegisterAt(instruction, 0));
+ break;
+ case Primitive::kPrimInt:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ Mov(dst.V4S(), 0, InputRegisterAt(instruction, 0));
+ break;
+ case Primitive::kPrimLong:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ Mov(dst.V2D(), 0, InputRegisterAt(instruction, 0));
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
+}
+
void LocationsBuilderARM64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr);
switch (instr->GetPackedType()) {
diff --git a/compiler/optimizing/code_generator_vector_arm_vixl.cc b/compiler/optimizing/code_generator_vector_arm_vixl.cc
index 527691d..7a11dff 100644
--- a/compiler/optimizing/code_generator_vector_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_vector_arm_vixl.cc
@@ -73,19 +73,11 @@
}
}
-void LocationsBuilderARMVIXL::VisitVecSetScalars(HVecSetScalars* instruction) {
+void LocationsBuilderARMVIXL::VisitVecExtractScalar(HVecExtractScalar* instruction) {
LOG(FATAL) << "No SIMD for " << instruction->GetId();
}
-void InstructionCodeGeneratorARMVIXL::VisitVecSetScalars(HVecSetScalars* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARMVIXL::VisitVecSumReduce(HVecSumReduce* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorARMVIXL::VisitVecSumReduce(HVecSumReduce* instruction) {
+void InstructionCodeGeneratorARMVIXL::VisitVecExtractScalar(HVecExtractScalar* instruction) {
LOG(FATAL) << "No SIMD for " << instruction->GetId();
}
@@ -112,6 +104,14 @@
}
}
+void LocationsBuilderARMVIXL::VisitVecReduce(HVecReduce* instruction) {
+ CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecReduce(HVecReduce* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
void LocationsBuilderARMVIXL::VisitVecCnv(HVecCnv* instruction) {
CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
}
@@ -621,6 +621,14 @@
}
}
+void LocationsBuilderARMVIXL::VisitVecSetScalars(HVecSetScalars* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecSetScalars(HVecSetScalars* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
void LocationsBuilderARMVIXL::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) {
LOG(FATAL) << "No SIMD for " << instr->GetId();
}
diff --git a/compiler/optimizing/code_generator_vector_mips.cc b/compiler/optimizing/code_generator_vector_mips.cc
index 6bf28ab..c2fbf7f 100644
--- a/compiler/optimizing/code_generator_vector_mips.cc
+++ b/compiler/optimizing/code_generator_vector_mips.cc
@@ -88,19 +88,11 @@
}
}
-void LocationsBuilderMIPS::VisitVecSetScalars(HVecSetScalars* instruction) {
+void LocationsBuilderMIPS::VisitVecExtractScalar(HVecExtractScalar* instruction) {
LOG(FATAL) << "No SIMD for " << instruction->GetId();
}
-void InstructionCodeGeneratorMIPS::VisitVecSetScalars(HVecSetScalars* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderMIPS::VisitVecSumReduce(HVecSumReduce* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorMIPS::VisitVecSumReduce(HVecSumReduce* instruction) {
+void InstructionCodeGeneratorMIPS::VisitVecExtractScalar(HVecExtractScalar* instruction) {
LOG(FATAL) << "No SIMD for " << instruction->GetId();
}
@@ -133,6 +125,14 @@
}
}
+void LocationsBuilderMIPS::VisitVecReduce(HVecReduce* instruction) {
+ CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecReduce(HVecReduce* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
void LocationsBuilderMIPS::VisitVecCnv(HVecCnv* instruction) {
CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
}
@@ -818,6 +818,14 @@
}
}
+void LocationsBuilderMIPS::VisitVecSetScalars(HVecSetScalars* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecSetScalars(HVecSetScalars* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
void LocationsBuilderMIPS::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr);
switch (instr->GetPackedType()) {
diff --git a/compiler/optimizing/code_generator_vector_mips64.cc b/compiler/optimizing/code_generator_vector_mips64.cc
index 75bf7a7..9d3a777 100644
--- a/compiler/optimizing/code_generator_vector_mips64.cc
+++ b/compiler/optimizing/code_generator_vector_mips64.cc
@@ -91,19 +91,11 @@
}
}
-void LocationsBuilderMIPS64::VisitVecSetScalars(HVecSetScalars* instruction) {
+void LocationsBuilderMIPS64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
LOG(FATAL) << "No SIMD for " << instruction->GetId();
}
-void InstructionCodeGeneratorMIPS64::VisitVecSetScalars(HVecSetScalars* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderMIPS64::VisitVecSumReduce(HVecSumReduce* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorMIPS64::VisitVecSumReduce(HVecSumReduce* instruction) {
+void InstructionCodeGeneratorMIPS64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
LOG(FATAL) << "No SIMD for " << instruction->GetId();
}
@@ -136,6 +128,14 @@
}
}
+void LocationsBuilderMIPS64::VisitVecReduce(HVecReduce* instruction) {
+ CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecReduce(HVecReduce* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
void LocationsBuilderMIPS64::VisitVecCnv(HVecCnv* instruction) {
CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
}
@@ -822,6 +822,14 @@
}
}
+void LocationsBuilderMIPS64::VisitVecSetScalars(HVecSetScalars* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecSetScalars(HVecSetScalars* instruction) {
+ LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
void LocationsBuilderMIPS64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr);
switch (instr->GetPackedType()) {
diff --git a/compiler/optimizing/code_generator_vector_x86.cc b/compiler/optimizing/code_generator_vector_x86.cc
index e7aec76..57e7dc6 100644
--- a/compiler/optimizing/code_generator_vector_x86.cc
+++ b/compiler/optimizing/code_generator_vector_x86.cc
@@ -27,9 +27,97 @@
void LocationsBuilderX86::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ HInstruction* input = instruction->InputAt(0);
+ bool is_zero = IsZeroBitPattern(input);
switch (instruction->GetPackedType()) {
case Primitive::kPrimLong:
- // Long needs extra temporary to load the register pair.
+ // Long needs extra temporary to load from the register pair.
+ if (!is_zero) {
+ locations->AddTemp(Location::RequiresFpuRegister());
+ }
+ FALLTHROUGH_INTENDED;
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+ : Location::RequiresRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ break;
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+ : Location::RequiresFpuRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
+}
+
+void InstructionCodeGeneratorX86::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+
+ // Shorthand for any type of zero.
+ if (IsZeroBitPattern(instruction->InputAt(0))) {
+ __ xorps(dst, dst);
+ return;
+ }
+
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ DCHECK_EQ(16u, instruction->GetVectorLength());
+ __ movd(dst, locations->InAt(0).AsRegister<Register>());
+ __ punpcklbw(dst, dst);
+ __ punpcklwd(dst, dst);
+ __ pshufd(dst, dst, Immediate(0));
+ break;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ __ movd(dst, locations->InAt(0).AsRegister<Register>());
+ __ punpcklwd(dst, dst);
+ __ pshufd(dst, dst, Immediate(0));
+ break;
+ case Primitive::kPrimInt:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ movd(dst, locations->InAt(0).AsRegister<Register>());
+ __ pshufd(dst, dst, Immediate(0));
+ break;
+ case Primitive::kPrimLong: {
+ XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ movd(dst, locations->InAt(0).AsRegisterPairLow<Register>());
+ __ movd(tmp, locations->InAt(0).AsRegisterPairHigh<Register>());
+ __ punpckldq(dst, tmp);
+ __ punpcklqdq(dst, dst);
+ break;
+ }
+ case Primitive::kPrimFloat:
+ DCHECK(locations->InAt(0).Equals(locations->Out()));
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ shufps(dst, dst, Immediate(0));
+ break;
+ case Primitive::kPrimDouble:
+ DCHECK(locations->InAt(0).Equals(locations->Out()));
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ shufpd(dst, dst, Immediate(0));
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
+}
+
+void LocationsBuilderX86::VisitVecExtractScalar(HVecExtractScalar* instruction) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimLong:
+ // Long needs extra temporary to store into the register pair.
locations->AddTemp(Location::RequiresFpuRegister());
FALLTHROUGH_INTENDED;
case Primitive::kPrimBoolean:
@@ -37,8 +125,8 @@
case Primitive::kPrimChar:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresFpuRegister());
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresRegister());
break;
case Primitive::kPrimFloat:
case Primitive::kPrimDouble:
@@ -51,48 +139,34 @@
}
}
-void InstructionCodeGeneratorX86::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+void InstructionCodeGeneratorX86::VisitVecExtractScalar(HVecExtractScalar* instruction) {
LocationSummary* locations = instruction->GetLocations();
- XmmRegister reg = locations->Out().AsFpuRegister<XmmRegister>();
+ XmmRegister src = locations->InAt(0).AsFpuRegister<XmmRegister>();
switch (instruction->GetPackedType()) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
- DCHECK_EQ(16u, instruction->GetVectorLength());
- __ movd(reg, locations->InAt(0).AsRegister<Register>());
- __ punpcklbw(reg, reg);
- __ punpcklwd(reg, reg);
- __ pshufd(reg, reg, Immediate(0));
- break;
case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- DCHECK_EQ(8u, instruction->GetVectorLength());
- __ movd(reg, locations->InAt(0).AsRegister<Register>());
- __ punpcklwd(reg, reg);
- __ pshufd(reg, reg, Immediate(0));
- break;
+ case Primitive::kPrimShort: // TODO: up to here, and?
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
case Primitive::kPrimInt:
- DCHECK_EQ(4u, instruction->GetVectorLength());
- __ movd(reg, locations->InAt(0).AsRegister<Register>());
- __ pshufd(reg, reg, Immediate(0));
+ DCHECK_LE(4u, instruction->GetVectorLength());
+ DCHECK_LE(instruction->GetVectorLength(), 16u);
+ __ movd(locations->Out().AsRegister<Register>(), src);
break;
case Primitive::kPrimLong: {
XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
DCHECK_EQ(2u, instruction->GetVectorLength());
- __ movd(reg, locations->InAt(0).AsRegisterPairLow<Register>());
- __ movd(tmp, locations->InAt(0).AsRegisterPairHigh<Register>());
- __ punpckldq(reg, tmp);
- __ punpcklqdq(reg, reg);
+ __ movd(locations->Out().AsRegisterPairLow<Register>(), src);
+ __ pshufd(tmp, src, Immediate(1));
+ __ movd(locations->Out().AsRegisterPairHigh<Register>(), tmp);
break;
}
case Primitive::kPrimFloat:
- DCHECK(locations->InAt(0).Equals(locations->Out()));
- DCHECK_EQ(4u, instruction->GetVectorLength());
- __ shufps(reg, reg, Immediate(0));
- break;
case Primitive::kPrimDouble:
- DCHECK(locations->InAt(0).Equals(locations->Out()));
- DCHECK_EQ(2u, instruction->GetVectorLength());
- __ shufpd(reg, reg, Immediate(0));
+ DCHECK_LE(2u, instruction->GetVectorLength());
+ DCHECK_LE(instruction->GetVectorLength(), 4u);
+ DCHECK(locations->InAt(0).Equals(locations->Out())); // no code required
break;
default:
LOG(FATAL) << "Unsupported SIMD type";
@@ -100,22 +174,6 @@
}
}
-void LocationsBuilderX86::VisitVecSetScalars(HVecSetScalars* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorX86::VisitVecSetScalars(HVecSetScalars* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderX86::VisitVecSumReduce(HVecSumReduce* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorX86::VisitVecSumReduce(HVecSumReduce* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
// Helper to set up locations for vector unary operations.
static void CreateVecUnOpLocations(ArenaAllocator* arena, HVecUnaryOperation* instruction) {
LocationSummary* locations = new (arena) LocationSummary(instruction);
@@ -137,6 +195,73 @@
}
}
+void LocationsBuilderX86::VisitVecReduce(HVecReduce* instruction) {
+ CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+ // Long reduction or min/max require a temporary.
+ if (instruction->GetPackedType() == Primitive::kPrimLong ||
+ instruction->GetKind() == HVecReduce::kMin ||
+ instruction->GetKind() == HVecReduce::kMax) {
+ instruction->GetLocations()->AddTemp(Location::RequiresFpuRegister());
+ }
+}
+
+void InstructionCodeGeneratorX86::VisitVecReduce(HVecReduce* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ XmmRegister src = locations->InAt(0).AsFpuRegister<XmmRegister>();
+ XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimInt:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ switch (instruction->GetKind()) {
+ case HVecReduce::kSum:
+ __ movaps(dst, src);
+ __ phaddd(dst, dst);
+ __ phaddd(dst, dst);
+ break;
+ case HVecReduce::kMin: {
+ XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+ __ movaps(tmp, src);
+ __ movaps(dst, src);
+ __ psrldq(tmp, Immediate(8));
+ __ pminsd(dst, tmp);
+ __ psrldq(tmp, Immediate(4));
+ __ pminsd(dst, tmp);
+ break;
+ }
+ case HVecReduce::kMax: {
+ XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+ __ movaps(tmp, src);
+ __ movaps(dst, src);
+ __ psrldq(tmp, Immediate(8));
+ __ pmaxsd(dst, tmp);
+ __ psrldq(tmp, Immediate(4));
+ __ pmaxsd(dst, tmp);
+ break;
+ }
+ }
+ break;
+ case Primitive::kPrimLong: {
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+ switch (instruction->GetKind()) {
+ case HVecReduce::kSum:
+ __ movaps(tmp, src);
+ __ movaps(dst, src);
+ __ punpckhqdq(tmp, tmp);
+ __ paddq(dst, tmp);
+ break;
+ case HVecReduce::kMin:
+ case HVecReduce::kMax:
+ LOG(FATAL) << "Unsupported SIMD type";
+ }
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
+}
+
void LocationsBuilderX86::VisitVecCnv(HVecCnv* instruction) {
CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
}
@@ -821,6 +946,91 @@
}
}
+void LocationsBuilderX86::VisitVecSetScalars(HVecSetScalars* instruction) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+
+ DCHECK_EQ(1u, instruction->InputCount()); // only one input currently implemented
+
+ HInstruction* input = instruction->InputAt(0);
+ bool is_zero = IsZeroBitPattern(input);
+
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimLong:
+ // Long needs extra temporary to load from register pairs.
+ if (!is_zero) {
+ locations->AddTemp(Location::RequiresFpuRegister());
+ }
+ FALLTHROUGH_INTENDED;
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+ : Location::RequiresRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ break;
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+ : Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
+}
+
+void InstructionCodeGeneratorX86::VisitVecSetScalars(HVecSetScalars* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+
+ DCHECK_EQ(1u, instruction->InputCount()); // only one input currently implemented
+
+ // Zero out all other elements first.
+ __ xorps(dst, dst);
+
+ // Shorthand for any type of zero.
+ if (IsZeroBitPattern(instruction->InputAt(0))) {
+ return;
+ }
+
+ // Set required elements.
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort: // TODO: up to here, and?
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ case Primitive::kPrimInt:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ movd(dst, locations->InAt(0).AsRegister<Register>());
+ break;
+ case Primitive::kPrimLong: {
+ XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ xorps(tmp, tmp);
+ __ movd(dst, locations->InAt(0).AsRegisterPairLow<Register>());
+ __ movd(tmp, locations->InAt(0).AsRegisterPairHigh<Register>());
+ __ punpckldq(dst, tmp);
+ break;
+ }
+ case Primitive::kPrimFloat:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ movss(dst, locations->InAt(1).AsFpuRegister<XmmRegister>());
+ break;
+ case Primitive::kPrimDouble:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ movsd(dst, locations->InAt(1).AsFpuRegister<XmmRegister>());
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
+}
+
void LocationsBuilderX86::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) {
LOG(FATAL) << "No SIMD for " << instr->GetId();
}
@@ -868,6 +1078,7 @@
case 8: scale = TIMES_8; break;
default: break;
}
+ // Incorporate the string or array offset in the address computation.
uint32_t offset = is_string_char_at
? mirror::String::ValueOffset().Uint32Value()
: mirror::Array::DataOffset(size).Uint32Value();
@@ -902,7 +1113,7 @@
__ testb(Address(locations->InAt(0).AsRegister<Register>(), count_offset), Immediate(1));
__ j(kNotZero, ¬_compressed);
// Zero extend 8 compressed bytes into 8 chars.
- __ movsd(reg, VecAddress(locations, 1, /*is_string_char_at*/ true));
+ __ movsd(reg, VecAddress(locations, 1, instruction->IsStringCharAt()));
__ pxor(tmp, tmp);
__ punpcklbw(reg, tmp);
__ jmp(&done);
diff --git a/compiler/optimizing/code_generator_vector_x86_64.cc b/compiler/optimizing/code_generator_vector_x86_64.cc
index c7ee81c..efab0db 100644
--- a/compiler/optimizing/code_generator_vector_x86_64.cc
+++ b/compiler/optimizing/code_generator_vector_x86_64.cc
@@ -27,6 +27,8 @@
void LocationsBuilderX86_64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ HInstruction* input = instruction->InputAt(0);
+ bool is_zero = IsZeroBitPattern(input);
switch (instruction->GetPackedType()) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
@@ -34,11 +36,88 @@
case Primitive::kPrimShort:
case Primitive::kPrimInt:
case Primitive::kPrimLong:
- locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+ : Location::RequiresRegister());
locations->SetOut(Location::RequiresFpuRegister());
break;
case Primitive::kPrimFloat:
case Primitive::kPrimDouble:
+ locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+ : Location::RequiresFpuRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+
+ // Shorthand for any type of zero.
+ if (IsZeroBitPattern(instruction->InputAt(0))) {
+ __ xorps(dst, dst);
+ return;
+ }
+
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ DCHECK_EQ(16u, instruction->GetVectorLength());
+ __ movd(dst, locations->InAt(0).AsRegister<CpuRegister>());
+ __ punpcklbw(dst, dst);
+ __ punpcklwd(dst, dst);
+ __ pshufd(dst, dst, Immediate(0));
+ break;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ __ movd(dst, locations->InAt(0).AsRegister<CpuRegister>());
+ __ punpcklwd(dst, dst);
+ __ pshufd(dst, dst, Immediate(0));
+ break;
+ case Primitive::kPrimInt:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ movd(dst, locations->InAt(0).AsRegister<CpuRegister>());
+ __ pshufd(dst, dst, Immediate(0));
+ break;
+ case Primitive::kPrimLong:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ movd(dst, locations->InAt(0).AsRegister<CpuRegister>()); // is 64-bit
+ __ punpcklqdq(dst, dst);
+ break;
+ case Primitive::kPrimFloat:
+ DCHECK(locations->InAt(0).Equals(locations->Out()));
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ shufps(dst, dst, Immediate(0));
+ break;
+ case Primitive::kPrimDouble:
+ DCHECK(locations->InAt(0).Equals(locations->Out()));
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ shufpd(dst, dst, Immediate(0));
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
+}
+
+void LocationsBuilderX86_64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimLong:
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresRegister());
+ break;
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
locations->SetInAt(0, Location::RequiresFpuRegister());
locations->SetOut(Location::SameAsFirstInput());
break;
@@ -48,44 +127,29 @@
}
}
-void InstructionCodeGeneratorX86_64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+void InstructionCodeGeneratorX86_64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
LocationSummary* locations = instruction->GetLocations();
- XmmRegister reg = locations->Out().AsFpuRegister<XmmRegister>();
+ XmmRegister src = locations->InAt(0).AsFpuRegister<XmmRegister>();
switch (instruction->GetPackedType()) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
- DCHECK_EQ(16u, instruction->GetVectorLength());
- __ movd(reg, locations->InAt(0).AsRegister<CpuRegister>());
- __ punpcklbw(reg, reg);
- __ punpcklwd(reg, reg);
- __ pshufd(reg, reg, Immediate(0));
- break;
case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- DCHECK_EQ(8u, instruction->GetVectorLength());
- __ movd(reg, locations->InAt(0).AsRegister<CpuRegister>());
- __ punpcklwd(reg, reg);
- __ pshufd(reg, reg, Immediate(0));
- break;
+ case Primitive::kPrimShort: // TODO: up to here, and?
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
case Primitive::kPrimInt:
DCHECK_EQ(4u, instruction->GetVectorLength());
- __ movd(reg, locations->InAt(0).AsRegister<CpuRegister>());
- __ pshufd(reg, reg, Immediate(0));
+ __ movd(locations->Out().AsRegister<CpuRegister>(), src);
break;
case Primitive::kPrimLong:
DCHECK_EQ(2u, instruction->GetVectorLength());
- __ movd(reg, locations->InAt(0).AsRegister<CpuRegister>()); // is 64-bit
- __ punpcklqdq(reg, reg);
+ __ movd(locations->Out().AsRegister<CpuRegister>(), src); // is 64-bit
break;
case Primitive::kPrimFloat:
- DCHECK(locations->InAt(0).Equals(locations->Out()));
- DCHECK_EQ(4u, instruction->GetVectorLength());
- __ shufps(reg, reg, Immediate(0));
- break;
case Primitive::kPrimDouble:
- DCHECK(locations->InAt(0).Equals(locations->Out()));
- DCHECK_EQ(2u, instruction->GetVectorLength());
- __ shufpd(reg, reg, Immediate(0));
+ DCHECK_LE(2u, instruction->GetVectorLength());
+ DCHECK_LE(instruction->GetVectorLength(), 4u);
+ DCHECK(locations->InAt(0).Equals(locations->Out())); // no code required
break;
default:
LOG(FATAL) << "Unsupported SIMD type";
@@ -93,22 +157,6 @@
}
}
-void LocationsBuilderX86_64::VisitVecSetScalars(HVecSetScalars* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorX86_64::VisitVecSetScalars(HVecSetScalars* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderX86_64::VisitVecSumReduce(HVecSumReduce* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorX86_64::VisitVecSumReduce(HVecSumReduce* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
// Helper to set up locations for vector unary operations.
static void CreateVecUnOpLocations(ArenaAllocator* arena, HVecUnaryOperation* instruction) {
LocationSummary* locations = new (arena) LocationSummary(instruction);
@@ -130,6 +178,73 @@
}
}
+void LocationsBuilderX86_64::VisitVecReduce(HVecReduce* instruction) {
+ CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+ // Long reduction or min/max require a temporary.
+ if (instruction->GetPackedType() == Primitive::kPrimLong ||
+ instruction->GetKind() == HVecReduce::kMin ||
+ instruction->GetKind() == HVecReduce::kMax) {
+ instruction->GetLocations()->AddTemp(Location::RequiresFpuRegister());
+ }
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecReduce(HVecReduce* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ XmmRegister src = locations->InAt(0).AsFpuRegister<XmmRegister>();
+ XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimInt:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ switch (instruction->GetKind()) {
+ case HVecReduce::kSum:
+ __ movaps(dst, src);
+ __ phaddd(dst, dst);
+ __ phaddd(dst, dst);
+ break;
+ case HVecReduce::kMin: {
+ XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+ __ movaps(tmp, src);
+ __ movaps(dst, src);
+ __ psrldq(tmp, Immediate(8));
+ __ pminsd(dst, tmp);
+ __ psrldq(tmp, Immediate(4));
+ __ pminsd(dst, tmp);
+ break;
+ }
+ case HVecReduce::kMax: {
+ XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+ __ movaps(tmp, src);
+ __ movaps(dst, src);
+ __ psrldq(tmp, Immediate(8));
+ __ pmaxsd(dst, tmp);
+ __ psrldq(tmp, Immediate(4));
+ __ pmaxsd(dst, tmp);
+ break;
+ }
+ }
+ break;
+ case Primitive::kPrimLong: {
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+ switch (instruction->GetKind()) {
+ case HVecReduce::kSum:
+ __ movaps(tmp, src);
+ __ movaps(dst, src);
+ __ punpckhqdq(tmp, tmp);
+ __ paddq(dst, tmp);
+ break;
+ case HVecReduce::kMin:
+ case HVecReduce::kMax:
+ LOG(FATAL) << "Unsupported SIMD type";
+ }
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
+}
+
void LocationsBuilderX86_64::VisitVecCnv(HVecCnv* instruction) {
CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
}
@@ -814,6 +929,81 @@
}
}
+void LocationsBuilderX86_64::VisitVecSetScalars(HVecSetScalars* instruction) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+
+ DCHECK_EQ(1u, instruction->InputCount()); // only one input currently implemented
+
+ HInstruction* input = instruction->InputAt(0);
+ bool is_zero = IsZeroBitPattern(input);
+
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimLong:
+ locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+ : Location::RequiresRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ break;
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+ : Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecSetScalars(HVecSetScalars* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+
+ DCHECK_EQ(1u, instruction->InputCount()); // only one input currently implemented
+
+ // Zero out all other elements first.
+ __ xorps(dst, dst);
+
+ // Shorthand for any type of zero.
+ if (IsZeroBitPattern(instruction->InputAt(0))) {
+ return;
+ }
+
+ // Set required elements.
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort: // TODO: up to here, and?
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ case Primitive::kPrimInt:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ movd(dst, locations->InAt(0).AsRegister<CpuRegister>());
+ break;
+ case Primitive::kPrimLong:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ movd(dst, locations->InAt(0).AsRegister<CpuRegister>()); // is 64-bit
+ break;
+ case Primitive::kPrimFloat:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ movss(dst, locations->InAt(0).AsFpuRegister<XmmRegister>());
+ break;
+ case Primitive::kPrimDouble:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ movsd(dst, locations->InAt(0).AsFpuRegister<XmmRegister>());
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
+}
+
void LocationsBuilderX86_64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) {
LOG(FATAL) << "No SIMD for " << instr->GetId();
}
@@ -861,6 +1051,7 @@
case 8: scale = TIMES_8; break;
default: break;
}
+ // Incorporate the string or array offset in the address computation.
uint32_t offset = is_string_char_at
? mirror::String::ValueOffset().Uint32Value()
: mirror::Array::DataOffset(size).Uint32Value();
@@ -895,7 +1086,7 @@
__ testb(Address(locations->InAt(0).AsRegister<CpuRegister>(), count_offset), Immediate(1));
__ j(kNotZero, ¬_compressed);
// Zero extend 8 compressed bytes into 8 chars.
- __ movsd(reg, VecAddress(locations, 1, /*is_string_char_at*/ true));
+ __ movsd(reg, VecAddress(locations, 1, instruction->IsStringCharAt()));
__ pxor(tmp, tmp);
__ punpcklbw(reg, tmp);
__ jmp(&done);
diff --git a/compiler/optimizing/emit_swap_mips_test.cc b/compiler/optimizing/emit_swap_mips_test.cc
index 0d4e1c5..fa3c4df 100644
--- a/compiler/optimizing/emit_swap_mips_test.cc
+++ b/compiler/optimizing/emit_swap_mips_test.cc
@@ -91,7 +91,9 @@
return nullptr;
}
- void DriverWrapper(HParallelMove* move, std::string assembly_text, std::string test_name) {
+ void DriverWrapper(HParallelMove* move,
+ const std::string& assembly_text,
+ const std::string& test_name) {
codegen_->GetMoveResolver()->EmitNativeCode(move);
assembler_ = codegen_->GetAssembler();
assembler_->FinalizeCode();
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index a20ec3c..3035e46 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -501,6 +501,20 @@
StartAttributeStream("field_type") << iset->GetFieldType();
}
+ void VisitStaticFieldGet(HStaticFieldGet* sget) OVERRIDE {
+ StartAttributeStream("field_name") <<
+ sget->GetFieldInfo().GetDexFile().PrettyField(sget->GetFieldInfo().GetFieldIndex(),
+ /* with type */ false);
+ StartAttributeStream("field_type") << sget->GetFieldType();
+ }
+
+ void VisitStaticFieldSet(HStaticFieldSet* sset) OVERRIDE {
+ StartAttributeStream("field_name") <<
+ sset->GetFieldInfo().GetDexFile().PrettyField(sset->GetFieldInfo().GetFieldIndex(),
+ /* with type */ false);
+ StartAttributeStream("field_type") << sset->GetFieldType();
+ }
+
void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* field_access) OVERRIDE {
StartAttributeStream("field_type") << field_access->GetFieldType();
}
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index e5463bf..1ed1b75 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -566,14 +566,6 @@
MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
}
-static void CreateIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
- LocationSummary* locations = new (arena) LocationSummary(invoke,
- LocationSummary::kNoCall,
- kIntrinsified);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
static void GenAbsInteger(LocationSummary* locations,
bool is64bit,
MacroAssembler* masm) {
@@ -588,7 +580,7 @@
}
void IntrinsicLocationsBuilderARM64::VisitMathAbsInt(HInvoke* invoke) {
- CreateIntToInt(arena_, invoke);
+ CreateIntToIntLocations(arena_, invoke);
}
void IntrinsicCodeGeneratorARM64::VisitMathAbsInt(HInvoke* invoke) {
@@ -596,7 +588,7 @@
}
void IntrinsicLocationsBuilderARM64::VisitMathAbsLong(HInvoke* invoke) {
- CreateIntToInt(arena_, invoke);
+ CreateIntToIntLocations(arena_, invoke);
}
void IntrinsicCodeGeneratorARM64::VisitMathAbsLong(HInvoke* invoke) {
@@ -1641,12 +1633,13 @@
}
// Assertions that must hold in order to compare strings 8 bytes at a time.
+ // Ok to do this because strings are zero-padded to kObjectAlignment.
DCHECK_ALIGNED(value_offset, 8);
static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
if (const_string != nullptr &&
- const_string_length < (is_compressed ? kShortConstStringEqualsCutoffInBytes
- : kShortConstStringEqualsCutoffInBytes / 2u)) {
+ const_string_length <= (is_compressed ? kShortConstStringEqualsCutoffInBytes
+ : kShortConstStringEqualsCutoffInBytes / 2u)) {
// Load and compare the contents. Though we know the contents of the short const string
// at compile time, materializing constants may be more code than loading from memory.
int32_t offset = value_offset;
@@ -1654,7 +1647,7 @@
RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 8u);
temp = temp.X();
temp1 = temp1.X();
- while (remaining_bytes > 8u) {
+ while (remaining_bytes > sizeof(uint64_t)) {
Register temp2 = XRegisterFrom(locations->GetTemp(0));
__ Ldp(temp, temp1, MemOperand(str.X(), offset));
__ Ldp(temp2, out, MemOperand(arg.X(), offset));
@@ -1690,7 +1683,6 @@
temp1 = temp1.X();
Register temp2 = XRegisterFrom(locations->GetTemp(0));
// Loop to compare strings 8 bytes at a time starting at the front of the string.
- // Ok to do this because strings are zero-padded to kObjectAlignment.
__ Bind(&loop);
__ Ldr(out, MemOperand(str.X(), temp1));
__ Ldr(temp2, MemOperand(arg.X(), temp1));
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 8b4044d..d2dc88a 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -331,6 +331,14 @@
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
+static void CreateLongToLongLocationsWithOverlap(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
LocationSummary* locations = new (arena) LocationSummary(invoke,
LocationSummary::kNoCall,
@@ -375,11 +383,7 @@
}
void IntrinsicLocationsBuilderARMVIXL::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
- LocationSummary* locations = new (arena_) LocationSummary(invoke,
- LocationSummary::kNoCall,
- kIntrinsified);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+ CreateLongToLongLocationsWithOverlap(arena_, invoke);
}
void IntrinsicCodeGeneratorARMVIXL::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
@@ -417,11 +421,7 @@
}
void IntrinsicLocationsBuilderARMVIXL::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
- LocationSummary* locations = new (arena_) LocationSummary(invoke,
- LocationSummary::kNoCall,
- kIntrinsified);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ CreateIntToIntLocations(arena_, invoke);
}
void IntrinsicCodeGeneratorARMVIXL::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
@@ -429,11 +429,7 @@
}
void IntrinsicLocationsBuilderARMVIXL::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
- LocationSummary* locations = new (arena_) LocationSummary(invoke,
- LocationSummary::kNoCall,
- kIntrinsified);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+ CreateLongToLongLocationsWithOverlap(arena_, invoke);
}
void IntrinsicCodeGeneratorARMVIXL::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
@@ -1713,6 +1709,22 @@
}
}
+// The cut off for unrolling the loop in String.equals() intrinsic for const strings.
+// The normal loop plus the pre-header is 9 instructions (18-26 bytes) without string compression
+// and 12 instructions (24-32 bytes) with string compression. We can compare up to 4 bytes in 4
+// instructions (LDR+LDR+CMP+BNE) and up to 8 bytes in 6 instructions (LDRD+LDRD+CMP+BNE+CMP+BNE).
+// Allow up to 12 instructions (32 bytes) for the unrolled loop.
+constexpr size_t kShortConstStringEqualsCutoffInBytes = 16;
+
+static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_length) {
+ if (candidate->IsLoadString()) {
+ HLoadString* load_string = candidate->AsLoadString();
+ const DexFile& dex_file = load_string->GetDexFile();
+ return dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), utf16_length);
+ }
+ return nullptr;
+}
+
void IntrinsicLocationsBuilderARMVIXL::VisitStringEquals(HInvoke* invoke) {
LocationSummary* locations = new (arena_) LocationSummary(invoke,
LocationSummary::kNoCall,
@@ -1720,12 +1732,29 @@
InvokeRuntimeCallingConventionARMVIXL calling_convention;
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
+
// Temporary registers to store lengths of strings and for calculations.
// Using instruction cbz requires a low register, so explicitly set a temp to be R0.
locations->AddTemp(LocationFrom(r0));
- locations->AddTemp(Location::RequiresRegister());
- locations->AddTemp(Location::RequiresRegister());
+ // For the generic implementation and for long const strings we need an extra temporary.
+ // We do not need it for short const strings, up to 4 bytes, see code generation below.
+ uint32_t const_string_length = 0u;
+ const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
+ if (const_string == nullptr) {
+ const_string = GetConstString(invoke->InputAt(1), &const_string_length);
+ }
+ bool is_compressed =
+ mirror::kUseStringCompression &&
+ const_string != nullptr &&
+ mirror::String::DexFileStringAllASCII(const_string, const_string_length);
+ if (const_string == nullptr || const_string_length > (is_compressed ? 4u : 2u)) {
+ locations->AddTemp(Location::RequiresRegister());
+ }
+
+ // TODO: If the String.equals() is used only for an immediately following HIf, we can
+ // mark it as emitted-at-use-site and emit branches directly to the appropriate blocks.
+ // Then we shall need an extra temporary register instead of the output register.
locations->SetOut(Location::RequiresRegister());
}
@@ -1738,8 +1767,6 @@
vixl32::Register out = OutputRegister(invoke);
vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
- vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
- vixl32::Register temp2 = RegisterFrom(locations->GetTemp(2));
vixl32::Label loop;
vixl32::Label end;
@@ -1771,53 +1798,110 @@
// Receiver must be a string object, so its class field is equal to all strings' class fields.
// If the argument is a string object, its class field must be equal to receiver's class field.
__ Ldr(temp, MemOperand(str, class_offset));
- __ Ldr(temp1, MemOperand(arg, class_offset));
- __ Cmp(temp, temp1);
+ __ Ldr(out, MemOperand(arg, class_offset));
+ __ Cmp(temp, out);
__ B(ne, &return_false, /* far_target */ false);
}
- // Load `count` fields of this and argument strings.
- __ Ldr(temp, MemOperand(str, count_offset));
- __ Ldr(temp1, MemOperand(arg, count_offset));
- // Check if `count` fields are equal, return false if they're not.
- // Also compares the compression style, if differs return false.
- __ Cmp(temp, temp1);
- __ B(ne, &return_false, /* far_target */ false);
- // Return true if both strings are empty. Even with string compression `count == 0` means empty.
- static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
- "Expecting 0=compressed, 1=uncompressed");
- __ CompareAndBranchIfZero(temp, &return_true, /* far_target */ false);
+ // Check if one of the inputs is a const string. Do not special-case both strings
+ // being const, such cases should be handled by constant folding if needed.
+ uint32_t const_string_length = 0u;
+ const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
+ if (const_string == nullptr) {
+ const_string = GetConstString(invoke->InputAt(1), &const_string_length);
+ if (const_string != nullptr) {
+ std::swap(str, arg); // Make sure the const string is in `str`.
+ }
+ }
+ bool is_compressed =
+ mirror::kUseStringCompression &&
+ const_string != nullptr &&
+ mirror::String::DexFileStringAllASCII(const_string, const_string_length);
+
+ if (const_string != nullptr) {
+ // Load `count` field of the argument string and check if it matches the const string.
+ // Also compares the compression style, if differs return false.
+ __ Ldr(temp, MemOperand(arg, count_offset));
+ __ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed)));
+ __ B(ne, &return_false, /* far_target */ false);
+ } else {
+ // Load `count` fields of this and argument strings.
+ __ Ldr(temp, MemOperand(str, count_offset));
+ __ Ldr(out, MemOperand(arg, count_offset));
+ // Check if `count` fields are equal, return false if they're not.
+ // Also compares the compression style, if differs return false.
+ __ Cmp(temp, out);
+ __ B(ne, &return_false, /* far_target */ false);
+ }
// Assertions that must hold in order to compare strings 4 bytes at a time.
+ // Ok to do this because strings are zero-padded to kObjectAlignment.
DCHECK_ALIGNED(value_offset, 4);
static_assert(IsAligned<4>(kObjectAlignment), "String data must be aligned for fast compare.");
- if (mirror::kUseStringCompression) {
- // For string compression, calculate the number of bytes to compare (not chars).
- // This could in theory exceed INT32_MAX, so treat temp as unsigned.
- __ Lsrs(temp, temp, 1u); // Extract length and check compression flag.
- ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
- 2 * kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
- __ it(cs); // If uncompressed,
- __ add(cs, temp, temp, temp); // double the byte count.
+ if (const_string != nullptr &&
+ const_string_length <= (is_compressed ? kShortConstStringEqualsCutoffInBytes
+ : kShortConstStringEqualsCutoffInBytes / 2u)) {
+ // Load and compare the contents. Though we know the contents of the short const string
+ // at compile time, materializing constants may be more code than loading from memory.
+ int32_t offset = value_offset;
+ size_t remaining_bytes =
+ RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 4u);
+ while (remaining_bytes > sizeof(uint32_t)) {
+ vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
+ UseScratchRegisterScope scratch_scope(assembler->GetVIXLAssembler());
+ vixl32::Register temp2 = scratch_scope.Acquire();
+ __ Ldrd(temp, temp1, MemOperand(str, offset));
+ __ Ldrd(temp2, out, MemOperand(arg, offset));
+ __ Cmp(temp, temp2);
+ __ B(ne, &return_false, /* far_label */ false);
+ __ Cmp(temp1, out);
+ __ B(ne, &return_false, /* far_label */ false);
+ offset += 2u * sizeof(uint32_t);
+ remaining_bytes -= 2u * sizeof(uint32_t);
+ }
+ if (remaining_bytes != 0u) {
+ __ Ldr(temp, MemOperand(str, offset));
+ __ Ldr(out, MemOperand(arg, offset));
+ __ Cmp(temp, out);
+ __ B(ne, &return_false, /* far_label */ false);
+ }
+ } else {
+ // Return true if both strings are empty. Even with string compression `count == 0` means empty.
+ static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
+ "Expecting 0=compressed, 1=uncompressed");
+ __ CompareAndBranchIfZero(temp, &return_true, /* far_target */ false);
+
+ if (mirror::kUseStringCompression) {
+ // For string compression, calculate the number of bytes to compare (not chars).
+ // This could in theory exceed INT32_MAX, so treat temp as unsigned.
+ __ Lsrs(temp, temp, 1u); // Extract length and check compression flag.
+ ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
+ 2 * kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
+ __ it(cs); // If uncompressed,
+ __ add(cs, temp, temp, temp); // double the byte count.
+ }
+
+ vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
+ UseScratchRegisterScope scratch_scope(assembler->GetVIXLAssembler());
+ vixl32::Register temp2 = scratch_scope.Acquire();
+
+ // Store offset of string value in preparation for comparison loop.
+ __ Mov(temp1, value_offset);
+
+ // Loop to compare strings 4 bytes at a time starting at the front of the string.
+ __ Bind(&loop);
+ __ Ldr(out, MemOperand(str, temp1));
+ __ Ldr(temp2, MemOperand(arg, temp1));
+ __ Add(temp1, temp1, Operand::From(sizeof(uint32_t)));
+ __ Cmp(out, temp2);
+ __ B(ne, &return_false, /* far_target */ false);
+ // With string compression, we have compared 4 bytes, otherwise 2 chars.
+ __ Subs(temp, temp, mirror::kUseStringCompression ? 4 : 2);
+ __ B(hi, &loop, /* far_target */ false);
}
- // Store offset of string value in preparation for comparison loop.
- __ Mov(temp1, value_offset);
-
- // Loop to compare strings 4 bytes at a time starting at the front of the string.
- // Ok to do this because strings are zero-padded to kObjectAlignment.
- __ Bind(&loop);
- __ Ldr(out, MemOperand(str, temp1));
- __ Ldr(temp2, MemOperand(arg, temp1));
- __ Add(temp1, temp1, Operand::From(sizeof(uint32_t)));
- __ Cmp(out, temp2);
- __ B(ne, &return_false, /* far_target */ false);
- // With string compression, we have compared 4 bytes, otherwise 2 chars.
- __ Subs(temp, temp, mirror::kUseStringCompression ? 4 : 2);
- __ B(hi, &loop, /* far_target */ false);
-
// Return true and exit the function.
// If loop does not result in returning false, we return true.
__ Bind(&return_true);
@@ -2723,11 +2807,7 @@
}
void IntrinsicLocationsBuilderARMVIXL::VisitLongReverse(HInvoke* invoke) {
- LocationSummary* locations = new (arena_) LocationSummary(invoke,
- LocationSummary::kNoCall,
- kIntrinsified);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+ CreateLongToLongLocationsWithOverlap(arena_, invoke);
}
void IntrinsicCodeGeneratorARMVIXL::VisitLongReverse(HInvoke* invoke) {
@@ -2753,11 +2833,7 @@
}
void IntrinsicLocationsBuilderARMVIXL::VisitLongReverseBytes(HInvoke* invoke) {
- LocationSummary* locations = new (arena_) LocationSummary(invoke,
- LocationSummary::kNoCall,
- kIntrinsified);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+ CreateLongToLongLocationsWithOverlap(arena_, invoke);
}
void IntrinsicCodeGeneratorARMVIXL::VisitLongReverseBytes(HInvoke* invoke) {
@@ -2827,6 +2903,137 @@
GenBitCount(invoke, Primitive::kPrimLong, GetAssembler());
}
+static void GenHighestOneBit(HInvoke* invoke,
+ Primitive::Type type,
+ CodeGeneratorARMVIXL* codegen) {
+ DCHECK(Primitive::IsIntOrLongType(type));
+
+ ArmVIXLAssembler* assembler = codegen->GetAssembler();
+ UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
+ const vixl32::Register temp = temps.Acquire();
+
+ if (type == Primitive::kPrimLong) {
+ LocationSummary* locations = invoke->GetLocations();
+ Location in = locations->InAt(0);
+ Location out = locations->Out();
+
+ vixl32::Register in_reg_lo = LowRegisterFrom(in);
+ vixl32::Register in_reg_hi = HighRegisterFrom(in);
+ vixl32::Register out_reg_lo = LowRegisterFrom(out);
+ vixl32::Register out_reg_hi = HighRegisterFrom(out);
+
+ __ Mov(temp, 0x80000000); // Modified immediate.
+ __ Clz(out_reg_lo, in_reg_lo);
+ __ Clz(out_reg_hi, in_reg_hi);
+ __ Lsr(out_reg_lo, temp, out_reg_lo);
+ __ Lsrs(out_reg_hi, temp, out_reg_hi);
+
+ // Discard result for lowest 32 bits if highest 32 bits are not zero.
+ // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
+ // we check that the output is in a low register, so that a 16-bit MOV
+ // encoding can be used. If output is in a high register, then we generate
+ // 4 more bytes of code to avoid a branch.
+ Operand mov_src(0);
+ if (!out_reg_lo.IsLow()) {
+ __ Mov(LeaveFlags, temp, 0);
+ mov_src = Operand(temp);
+ }
+ ExactAssemblyScope it_scope(codegen->GetVIXLAssembler(),
+ 2 * vixl32::k16BitT32InstructionSizeInBytes,
+ CodeBufferCheckScope::kExactSize);
+ __ it(ne);
+ __ mov(ne, out_reg_lo, mov_src);
+ } else {
+ vixl32::Register out = OutputRegister(invoke);
+ vixl32::Register in = InputRegisterAt(invoke, 0);
+
+ __ Mov(temp, 0x80000000); // Modified immediate.
+ __ Clz(out, in);
+ __ Lsr(out, temp, out);
+ }
+}
+
+void IntrinsicLocationsBuilderARMVIXL::VisitIntegerHighestOneBit(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARMVIXL::VisitIntegerHighestOneBit(HInvoke* invoke) {
+ GenHighestOneBit(invoke, Primitive::kPrimInt, codegen_);
+}
+
+void IntrinsicLocationsBuilderARMVIXL::VisitLongHighestOneBit(HInvoke* invoke) {
+ CreateLongToLongLocationsWithOverlap(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARMVIXL::VisitLongHighestOneBit(HInvoke* invoke) {
+ GenHighestOneBit(invoke, Primitive::kPrimLong, codegen_);
+}
+
+static void GenLowestOneBit(HInvoke* invoke,
+ Primitive::Type type,
+ CodeGeneratorARMVIXL* codegen) {
+ DCHECK(Primitive::IsIntOrLongType(type));
+
+ ArmVIXLAssembler* assembler = codegen->GetAssembler();
+ UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
+ const vixl32::Register temp = temps.Acquire();
+
+ if (type == Primitive::kPrimLong) {
+ LocationSummary* locations = invoke->GetLocations();
+ Location in = locations->InAt(0);
+ Location out = locations->Out();
+
+ vixl32::Register in_reg_lo = LowRegisterFrom(in);
+ vixl32::Register in_reg_hi = HighRegisterFrom(in);
+ vixl32::Register out_reg_lo = LowRegisterFrom(out);
+ vixl32::Register out_reg_hi = HighRegisterFrom(out);
+
+ __ Rsb(out_reg_hi, in_reg_hi, 0);
+ __ Rsb(out_reg_lo, in_reg_lo, 0);
+ __ And(out_reg_hi, out_reg_hi, in_reg_hi);
+ // The result of this operation is 0 iff in_reg_lo is 0
+ __ Ands(out_reg_lo, out_reg_lo, in_reg_lo);
+
+ // Discard result for highest 32 bits if lowest 32 bits are not zero.
+ // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
+ // we check that the output is in a low register, so that a 16-bit MOV
+ // encoding can be used. If output is in a high register, then we generate
+ // 4 more bytes of code to avoid a branch.
+ Operand mov_src(0);
+ if (!out_reg_lo.IsLow()) {
+ __ Mov(LeaveFlags, temp, 0);
+ mov_src = Operand(temp);
+ }
+ ExactAssemblyScope it_scope(codegen->GetVIXLAssembler(),
+ 2 * vixl32::k16BitT32InstructionSizeInBytes,
+ CodeBufferCheckScope::kExactSize);
+ __ it(ne);
+ __ mov(ne, out_reg_hi, mov_src);
+ } else {
+ vixl32::Register out = OutputRegister(invoke);
+ vixl32::Register in = InputRegisterAt(invoke, 0);
+
+ __ Rsb(temp, in, 0);
+ __ And(out, temp, in);
+ }
+}
+
+void IntrinsicLocationsBuilderARMVIXL::VisitIntegerLowestOneBit(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARMVIXL::VisitIntegerLowestOneBit(HInvoke* invoke) {
+ GenLowestOneBit(invoke, Primitive::kPrimInt, codegen_);
+}
+
+void IntrinsicLocationsBuilderARMVIXL::VisitLongLowestOneBit(HInvoke* invoke) {
+ CreateLongToLongLocationsWithOverlap(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARMVIXL::VisitLongLowestOneBit(HInvoke* invoke) {
+ GenLowestOneBit(invoke, Primitive::kPrimLong, codegen_);
+}
+
void IntrinsicLocationsBuilderARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) {
LocationSummary* locations = new (arena_) LocationSummary(invoke,
LocationSummary::kNoCall,
@@ -3124,10 +3331,6 @@
UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeCASLong) // High register pressure.
UNIMPLEMENTED_INTRINSIC(ARMVIXL, SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(ARMVIXL, ReferenceGetReferent)
-UNIMPLEMENTED_INTRINSIC(ARMVIXL, IntegerHighestOneBit)
-UNIMPLEMENTED_INTRINSIC(ARMVIXL, LongHighestOneBit)
-UNIMPLEMENTED_INTRINSIC(ARMVIXL, IntegerLowestOneBit)
-UNIMPLEMENTED_INTRINSIC(ARMVIXL, LongLowestOneBit)
UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOf);
UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/load_store_analysis.h b/compiler/optimizing/load_store_analysis.h
index a2c1794..02bc254 100644
--- a/compiler/optimizing/load_store_analysis.h
+++ b/compiler/optimizing/load_store_analysis.h
@@ -461,49 +461,15 @@
has_heap_stores_ = true;
}
- void VisitNewInstance(HNewInstance* new_instance) OVERRIDE {
- // Any references appearing in the ref_info_array_ so far cannot alias with new_instance.
- CreateReferenceInfoForReferenceType(new_instance);
- }
-
- void VisitNewArray(HNewArray* new_array) OVERRIDE {
- // Any references appearing in the ref_info_array_ so far cannot alias with new_array.
- CreateReferenceInfoForReferenceType(new_array);
- }
-
- void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* instruction) OVERRIDE {
- CreateReferenceInfoForReferenceType(instruction);
- }
-
- void VisitInvokeVirtual(HInvokeVirtual* instruction) OVERRIDE {
- CreateReferenceInfoForReferenceType(instruction);
- }
-
- void VisitInvokeInterface(HInvokeInterface* instruction) OVERRIDE {
- CreateReferenceInfoForReferenceType(instruction);
- }
-
- void VisitInvokeUnresolved(HInvokeUnresolved* instruction) OVERRIDE {
- CreateReferenceInfoForReferenceType(instruction);
- }
-
- void VisitInvokePolymorphic(HInvokePolymorphic* instruction) OVERRIDE {
- CreateReferenceInfoForReferenceType(instruction);
- }
-
- void VisitLoadString(HLoadString* instruction) OVERRIDE {
- CreateReferenceInfoForReferenceType(instruction);
- }
-
- void VisitPhi(HPhi* instruction) OVERRIDE {
- CreateReferenceInfoForReferenceType(instruction);
- }
-
- void VisitParameterValue(HParameterValue* instruction) OVERRIDE {
- CreateReferenceInfoForReferenceType(instruction);
- }
-
- void VisitSelect(HSelect* instruction) OVERRIDE {
+ void VisitInstruction(HInstruction* instruction) OVERRIDE {
+ // Any new-instance or new-array cannot alias with references that
+ // pre-exist the new-instance/new-array. We append entries into
+ // ref_info_array_ which keeps track of the order of creation
+ // of reference values since we visit the blocks in reverse post order.
+ //
+ // By default, VisitXXX() (including VisitPhi()) calls VisitInstruction(),
+ // unless VisitXXX() is overridden. VisitInstanceFieldGet() etc. above
+ // also call CreateReferenceInfoForReferenceType() explicitly.
CreateReferenceInfoForReferenceType(instruction);
}
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 027ba77..f8f4eb2 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -285,6 +285,19 @@
return false;
}
+// Translates operation to reduction kind.
+static HVecReduce::ReductionKind GetReductionKind(HInstruction* reduction) {
+ if (reduction->IsVecAdd() || reduction->IsVecSub()) {
+ return HVecReduce::kSum;
+ } else if (reduction->IsVecMin()) {
+ return HVecReduce::kMin;
+ } else if (reduction->IsVecMax()) {
+ return HVecReduce::kMax;
+ }
+ LOG(FATAL) << "Unsupported SIMD reduction";
+ UNREACHABLE();
+}
+
// Test vector restrictions.
static bool HasVectorRestrictions(uint64_t restrictions, uint64_t tested) {
return (restrictions & tested) != 0;
@@ -334,7 +347,8 @@
vector_peeling_candidate_(nullptr),
vector_runtime_test_a_(nullptr),
vector_runtime_test_b_(nullptr),
- vector_map_(nullptr) {
+ vector_map_(nullptr),
+ vector_permanent_map_(nullptr) {
}
void HLoopOptimization::Run() {
@@ -388,11 +402,14 @@
ArenaSet<ArrayReference> refs(loop_allocator_->Adapter(kArenaAllocLoopOptimization));
ArenaSafeMap<HInstruction*, HInstruction*> map(
std::less<HInstruction*>(), loop_allocator_->Adapter(kArenaAllocLoopOptimization));
+ ArenaSafeMap<HInstruction*, HInstruction*> perm(
+ std::less<HInstruction*>(), loop_allocator_->Adapter(kArenaAllocLoopOptimization));
// Attach.
iset_ = &iset;
reductions_ = &reds;
vector_refs_ = &refs;
vector_map_ = ↦
+ vector_permanent_map_ = &perm;
// Traverse.
TraverseLoopsInnerToOuter(top_loop_);
// Detach.
@@ -400,6 +417,7 @@
reductions_ = nullptr;
vector_refs_ = nullptr;
vector_map_ = nullptr;
+ vector_permanent_map_ = nullptr;
}
}
@@ -603,7 +621,6 @@
// Vectorize loop, if possible and valid.
if (kEnableVectorization &&
TrySetSimpleLoopHeader(header, &main_phi) &&
- reductions_->empty() && // TODO: possible with some effort
ShouldVectorize(node, body, trip_count) &&
TryAssignLastValue(node->loop_info, main_phi, preheader, /*collect_loop_uses*/ true)) {
Vectorize(node, body, exit, trip_count);
@@ -802,6 +819,13 @@
/*unroll*/ 1);
}
+ // Link reductions to their final uses.
+ for (auto i = reductions_->begin(); i != reductions_->end(); ++i) {
+ if (i->first->IsPhi()) {
+ i->first->ReplaceWith(ReduceAndExtractIfNeeded(i->second));
+ }
+ }
+
// Remove the original loop by disconnecting the body block
// and removing all instructions from the header.
block->DisconnectAndDelete();
@@ -841,21 +865,10 @@
vector_header_->AddInstruction(cond);
vector_header_->AddInstruction(new (global_allocator_) HIf(cond));
vector_index_ = phi;
+ vector_permanent_map_->clear(); // preserved over unrolling
for (uint32_t u = 0; u < unroll; u++) {
- // Clear map, leaving loop invariants setup during unrolling.
- if (u == 0) {
- vector_map_->clear();
- } else {
- for (auto i = vector_map_->begin(); i != vector_map_->end(); ) {
- if (i->second->IsVecReplicateScalar()) {
- DCHECK(node->loop_info->IsDefinedOutOfTheLoop(i->first));
- ++i;
- } else {
- i = vector_map_->erase(i);
- }
- }
- }
// Generate instruction map.
+ vector_map_->clear();
for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
bool vectorized_def = VectorizeDef(node, it.Current(), /*generate_code*/ true);
DCHECK(vectorized_def);
@@ -872,9 +885,17 @@
}
}
}
+ // Generate the induction.
vector_index_ = new (global_allocator_) HAdd(induc_type, vector_index_, step);
Insert(vector_body_, vector_index_);
}
+ // Finalize phi inputs for the reductions (if any).
+ for (auto i = reductions_->begin(); i != reductions_->end(); ++i) {
+ if (!i->first->IsPhi()) {
+ DCHECK(i->second->IsPhi());
+ GenerateVecReductionPhiInputs(i->second->AsPhi(), i->first);
+ }
+ }
// Finalize phi inputs for the loop index.
phi->AddInput(lo);
phi->AddInput(vector_index_);
@@ -910,6 +931,23 @@
}
return false;
}
+ // Accept a left-hand-side reduction for
+ // (1) supported vector type,
+ // (2) vectorizable right-hand-side value.
+ auto redit = reductions_->find(instruction);
+ if (redit != reductions_->end()) {
+ Primitive::Type type = instruction->GetType();
+ if (TrySetVectorType(type, &restrictions) &&
+ VectorizeUse(node, instruction, generate_code, type, restrictions)) {
+ if (generate_code) {
+ HInstruction* new_red = vector_map_->Get(instruction);
+ vector_permanent_map_->Put(new_red, vector_map_->Get(redit->second));
+ vector_permanent_map_->Overwrite(redit->second, new_red);
+ }
+ return true;
+ }
+ return false;
+ }
// Branch back okay.
if (instruction->IsGoto()) {
return true;
@@ -965,6 +1003,21 @@
}
return true;
}
+ } else if (instruction->IsPhi()) {
+ // Accept particular phi operations.
+ if (reductions_->find(instruction) != reductions_->end()) {
+ // Deal with vector restrictions.
+ if (HasVectorRestrictions(restrictions, kNoReduction)) {
+ return false;
+ }
+ // Accept a reduction.
+ if (generate_code) {
+ GenerateVecReductionPhi(instruction->AsPhi());
+ }
+ return true;
+ }
+ // TODO: accept right-hand-side induction?
+ return false;
} else if (instruction->IsTypeConversion()) {
// Accept particular type conversions.
HTypeConversion* conversion = instruction->AsTypeConversion();
@@ -1155,14 +1208,14 @@
switch (type) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
- *restrictions |= kNoDiv;
+ *restrictions |= kNoDiv | kNoReduction;
return TrySetVectorLength(8);
case Primitive::kPrimChar:
case Primitive::kPrimShort:
- *restrictions |= kNoDiv | kNoStringCharAt;
+ *restrictions |= kNoDiv | kNoStringCharAt | kNoReduction;
return TrySetVectorLength(4);
case Primitive::kPrimInt:
- *restrictions |= kNoDiv;
+ *restrictions |= kNoDiv | kNoReduction;
return TrySetVectorLength(2);
default:
break;
@@ -1174,11 +1227,11 @@
switch (type) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
- *restrictions |= kNoDiv;
+ *restrictions |= kNoDiv | kNoReduction;
return TrySetVectorLength(16);
case Primitive::kPrimChar:
case Primitive::kPrimShort:
- *restrictions |= kNoDiv;
+ *restrictions |= kNoDiv | kNoReduction;
return TrySetVectorLength(8);
case Primitive::kPrimInt:
*restrictions |= kNoDiv;
@@ -1187,8 +1240,10 @@
*restrictions |= kNoDiv | kNoMul | kNoMinMax;
return TrySetVectorLength(2);
case Primitive::kPrimFloat:
+ *restrictions |= kNoReduction;
return TrySetVectorLength(4);
case Primitive::kPrimDouble:
+ *restrictions |= kNoReduction;
return TrySetVectorLength(2);
default:
return false;
@@ -1200,11 +1255,12 @@
switch (type) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
- *restrictions |= kNoMul | kNoDiv | kNoShift | kNoAbs | kNoSignedHAdd | kNoUnroundedHAdd;
+ *restrictions |=
+ kNoMul | kNoDiv | kNoShift | kNoAbs | kNoSignedHAdd | kNoUnroundedHAdd | kNoReduction;
return TrySetVectorLength(16);
case Primitive::kPrimChar:
case Primitive::kPrimShort:
- *restrictions |= kNoDiv | kNoAbs | kNoSignedHAdd | kNoUnroundedHAdd;
+ *restrictions |= kNoDiv | kNoAbs | kNoSignedHAdd | kNoUnroundedHAdd | kNoReduction;
return TrySetVectorLength(8);
case Primitive::kPrimInt:
*restrictions |= kNoDiv;
@@ -1213,10 +1269,10 @@
*restrictions |= kNoMul | kNoDiv | kNoShr | kNoAbs | kNoMinMax;
return TrySetVectorLength(2);
case Primitive::kPrimFloat:
- *restrictions |= kNoMinMax; // -0.0 vs +0.0
+ *restrictions |= kNoMinMax | kNoReduction; // minmax: -0.0 vs +0.0
return TrySetVectorLength(4);
case Primitive::kPrimDouble:
- *restrictions |= kNoMinMax; // -0.0 vs +0.0
+ *restrictions |= kNoMinMax | kNoReduction; // minmax: -0.0 vs +0.0
return TrySetVectorLength(2);
default:
break;
@@ -1228,23 +1284,23 @@
switch (type) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
- *restrictions |= kNoDiv;
+ *restrictions |= kNoDiv | kNoReduction;
return TrySetVectorLength(16);
case Primitive::kPrimChar:
case Primitive::kPrimShort:
- *restrictions |= kNoDiv | kNoStringCharAt;
+ *restrictions |= kNoDiv | kNoStringCharAt | kNoReduction;
return TrySetVectorLength(8);
case Primitive::kPrimInt:
- *restrictions |= kNoDiv;
+ *restrictions |= kNoDiv | kNoReduction;
return TrySetVectorLength(4);
case Primitive::kPrimLong:
- *restrictions |= kNoDiv;
+ *restrictions |= kNoDiv | kNoReduction;
return TrySetVectorLength(2);
case Primitive::kPrimFloat:
- *restrictions |= kNoMinMax; // min/max(x, NaN)
+ *restrictions |= kNoMinMax | kNoReduction; // min/max(x, NaN)
return TrySetVectorLength(4);
case Primitive::kPrimDouble:
- *restrictions |= kNoMinMax; // min/max(x, NaN)
+ *restrictions |= kNoMinMax | kNoReduction; // min/max(x, NaN)
return TrySetVectorLength(2);
default:
break;
@@ -1256,23 +1312,23 @@
switch (type) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
- *restrictions |= kNoDiv;
+ *restrictions |= kNoDiv | kNoReduction;
return TrySetVectorLength(16);
case Primitive::kPrimChar:
case Primitive::kPrimShort:
- *restrictions |= kNoDiv | kNoStringCharAt;
+ *restrictions |= kNoDiv | kNoStringCharAt | kNoReduction;
return TrySetVectorLength(8);
case Primitive::kPrimInt:
- *restrictions |= kNoDiv;
+ *restrictions |= kNoDiv | kNoReduction;
return TrySetVectorLength(4);
case Primitive::kPrimLong:
- *restrictions |= kNoDiv;
+ *restrictions |= kNoDiv | kNoReduction;
return TrySetVectorLength(2);
case Primitive::kPrimFloat:
- *restrictions |= kNoMinMax; // min/max(x, NaN)
+ *restrictions |= kNoMinMax | kNoReduction; // min/max(x, NaN)
return TrySetVectorLength(4);
case Primitive::kPrimDouble:
- *restrictions |= kNoMinMax; // min/max(x, NaN)
+ *restrictions |= kNoMinMax | kNoReduction; // min/max(x, NaN)
return TrySetVectorLength(2);
default:
break;
@@ -1305,9 +1361,16 @@
return;
}
// In vector code, explicit scalar expansion is needed.
- HInstruction* vector = new (global_allocator_) HVecReplicateScalar(
- global_allocator_, org, type, vector_length_);
- vector_map_->Put(org, Insert(vector_preheader_, vector));
+ HInstruction* vector = nullptr;
+ auto it = vector_permanent_map_->find(org);
+ if (it != vector_permanent_map_->end()) {
+ vector = it->second; // reuse during unrolling
+ } else {
+ vector = new (global_allocator_) HVecReplicateScalar(
+ global_allocator_, org, type, vector_length_);
+ vector_permanent_map_->Put(org, Insert(vector_preheader_, vector));
+ }
+ vector_map_->Put(org, vector);
}
}
@@ -1362,6 +1425,78 @@
vector_map_->Put(org, vector);
}
+void HLoopOptimization::GenerateVecReductionPhi(HPhi* phi) {
+ DCHECK(reductions_->find(phi) != reductions_->end());
+ DCHECK(reductions_->Get(phi->InputAt(1)) == phi);
+ HInstruction* vector = nullptr;
+ if (vector_mode_ == kSequential) {
+ HPhi* new_phi = new (global_allocator_) HPhi(
+ global_allocator_, kNoRegNumber, 0, phi->GetType());
+ vector_header_->AddPhi(new_phi);
+ vector = new_phi;
+ } else {
+ // Link vector reduction back to prior unrolled update, or a first phi.
+ auto it = vector_permanent_map_->find(phi);
+ if (it != vector_permanent_map_->end()) {
+ vector = it->second;
+ } else {
+ HPhi* new_phi = new (global_allocator_) HPhi(
+ global_allocator_, kNoRegNumber, 0, HVecOperation::kSIMDType);
+ vector_header_->AddPhi(new_phi);
+ vector = new_phi;
+ }
+ }
+ vector_map_->Put(phi, vector);
+}
+
+void HLoopOptimization::GenerateVecReductionPhiInputs(HPhi* phi, HInstruction* reduction) {
+ HInstruction* new_phi = vector_map_->Get(phi);
+ HInstruction* new_init = reductions_->Get(phi);
+ HInstruction* new_red = vector_map_->Get(reduction);
+ // Link unrolled vector loop back to new phi.
+ for (; !new_phi->IsPhi(); new_phi = vector_permanent_map_->Get(new_phi)) {
+ DCHECK(new_phi->IsVecOperation());
+ }
+ // Prepare the new initialization.
+ if (vector_mode_ == kVector) {
+ // Generate a [initial, 0, .., 0] vector.
+ new_init = Insert(
+ vector_preheader_,
+ new (global_allocator_) HVecSetScalars(
+ global_allocator_, &new_init, phi->GetType(), vector_length_, 1));
+ } else {
+ new_init = ReduceAndExtractIfNeeded(new_init);
+ }
+ // Set the phi inputs.
+ DCHECK(new_phi->IsPhi());
+ new_phi->AsPhi()->AddInput(new_init);
+ new_phi->AsPhi()->AddInput(new_red);
+ // New feed value for next phi (safe mutation in iteration).
+ reductions_->find(phi)->second = new_phi;
+}
+
+HInstruction* HLoopOptimization::ReduceAndExtractIfNeeded(HInstruction* instruction) {
+ if (instruction->IsPhi()) {
+ HInstruction* input = instruction->InputAt(1);
+ if (input->IsVecOperation()) {
+ Primitive::Type type = input->AsVecOperation()->GetPackedType();
+ HBasicBlock* exit = instruction->GetBlock()->GetSuccessors()[0];
+ // Generate a vector reduction and scalar extract
+ // x = REDUCE( [x_1, .., x_n] )
+ // y = x_1
+ // along the exit of the defining loop.
+ HVecReduce::ReductionKind kind = GetReductionKind(input);
+ HInstruction* reduce = new (global_allocator_) HVecReduce(
+ global_allocator_, instruction, type, vector_length_, kind);
+ exit->InsertInstructionBefore(reduce, exit->GetFirstInstruction());
+ instruction = new (global_allocator_) HVecExtractScalar(
+ global_allocator_, reduce, type, vector_length_, 0);
+ exit->InsertInstructionAfter(instruction, reduce);
+ }
+ }
+ return instruction;
+}
+
#define GENERATE_VEC(x, y) \
if (vector_mode_ == kVector) { \
vector = (x); \
@@ -1542,10 +1677,9 @@
// Test for top level arithmetic shift right x >> 1 or logical shift right x >>> 1
// (note whether the sign bit in wider precision is shifted in has no effect
// on the narrow precision computed by the idiom).
- int64_t distance = 0;
if ((instruction->IsShr() ||
instruction->IsUShr()) &&
- IsInt64AndGet(instruction->InputAt(1), /*out*/ &distance) && distance == 1) {
+ IsInt64Value(instruction->InputAt(1), 1)) {
// Test for (a + b + c) >> 1 for optional constant c.
HInstruction* a = nullptr;
HInstruction* b = nullptr;
diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h
index 49be8a3..ba9126c 100644
--- a/compiler/optimizing/loop_optimization.h
+++ b/compiler/optimizing/loop_optimization.h
@@ -62,17 +62,18 @@
* Vectorization restrictions (bit mask).
*/
enum VectorRestrictions {
- kNone = 0, // no restrictions
- kNoMul = 1, // no multiplication
- kNoDiv = 2, // no division
- kNoShift = 4, // no shift
- kNoShr = 8, // no arithmetic shift right
- kNoHiBits = 16, // "wider" operations cannot bring in higher order bits
- kNoSignedHAdd = 32, // no signed halving add
- kNoUnroundedHAdd = 64, // no unrounded halving add
- kNoAbs = 128, // no absolute value
- kNoMinMax = 256, // no min/max
- kNoStringCharAt = 512, // no StringCharAt
+ kNone = 0, // no restrictions
+ kNoMul = 1 << 0, // no multiplication
+ kNoDiv = 1 << 1, // no division
+ kNoShift = 1 << 2, // no shift
+ kNoShr = 1 << 3, // no arithmetic shift right
+ kNoHiBits = 1 << 4, // "wider" operations cannot bring in higher order bits
+ kNoSignedHAdd = 1 << 5, // no signed halving add
+ kNoUnroundedHAdd = 1 << 6, // no unrounded halving add
+ kNoAbs = 1 << 7, // no absolute value
+ kNoMinMax = 1 << 8, // no min/max
+ kNoStringCharAt = 1 << 9, // no StringCharAt
+ kNoReduction = 1 << 10, // no reduction
};
/*
@@ -155,6 +156,9 @@
HInstruction* opb,
HInstruction* offset,
Primitive::Type type);
+ void GenerateVecReductionPhi(HPhi* phi);
+ void GenerateVecReductionPhiInputs(HPhi* phi, HInstruction* reduction);
+ HInstruction* ReduceAndExtractIfNeeded(HInstruction* instruction);
void GenerateVecOp(HInstruction* org,
HInstruction* opa,
HInstruction* opb,
@@ -253,6 +257,10 @@
// Contents reside in phase-local heap memory.
ArenaSafeMap<HInstruction*, HInstruction*>* vector_map_;
+ // Permanent mapping used during vectorization synthesis.
+ // Contents reside in phase-local heap memory.
+ ArenaSafeMap<HInstruction*, HInstruction*>* vector_permanent_map_;
+
// Temporary vectorization bookkeeping.
VectorMode vector_mode_; // synthesis mode
HBasicBlock* vector_preheader_; // preheader of the new loop
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index f60d532..869fdd4 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1374,7 +1374,8 @@
M(UShr, BinaryOperation) \
M(Xor, BinaryOperation) \
M(VecReplicateScalar, VecUnaryOperation) \
- M(VecSumReduce, VecUnaryOperation) \
+ M(VecExtractScalar, VecUnaryOperation) \
+ M(VecReduce, VecUnaryOperation) \
M(VecCnv, VecUnaryOperation) \
M(VecNeg, VecUnaryOperation) \
M(VecAbs, VecUnaryOperation) \
@@ -7030,6 +7031,17 @@
return false;
}
+// Returns true iff instruction is the given integral constant.
+inline bool IsInt64Value(HInstruction* instruction, int64_t value) {
+ int64_t val = 0;
+ return IsInt64AndGet(instruction, &val) && val == value;
+}
+
+// Returns true iff instruction is a zero bit pattern.
+inline bool IsZeroBitPattern(HInstruction* instruction) {
+ return instruction->IsConstant() && instruction->AsConstant()->IsZeroBitPattern();
+}
+
#define INSTRUCTION_TYPE_CHECK(type, super) \
inline bool HInstruction::Is##type() const { return GetKind() == k##type; } \
inline const H##type* HInstruction::As##type() const { \
diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h
index 6261171..886d75e 100644
--- a/compiler/optimizing/nodes_vector.h
+++ b/compiler/optimizing/nodes_vector.h
@@ -63,6 +63,10 @@
// GetVectorLength() x GetPackedType() operations simultaneously.
class HVecOperation : public HVariableInputSizeInstruction {
public:
+ // A SIMD operation looks like a FPU location.
+ // TODO: we could introduce SIMD types in HIR.
+ static constexpr Primitive::Type kSIMDType = Primitive::kPrimDouble;
+
HVecOperation(ArenaAllocator* arena,
Primitive::Type packed_type,
SideEffects side_effects,
@@ -89,10 +93,9 @@
return vector_length_ * Primitive::ComponentSize(GetPackedType());
}
- // Returns the type of the vector operation: a SIMD operation looks like a FPU location.
- // TODO: we could introduce SIMD types in HIR.
+ // Returns the type of the vector operation.
Primitive::Type GetType() const OVERRIDE {
- return Primitive::kPrimDouble;
+ return kSIMDType;
}
// Returns the true component type packed in a vector.
@@ -220,8 +223,11 @@
DISALLOW_COPY_AND_ASSIGN(HVecMemoryOperation);
};
-// Packed type consistency checker (same vector length integral types may mix freely).
+// Packed type consistency checker ("same vector length" integral types may mix freely).
inline static bool HasConsistentPackedTypes(HInstruction* input, Primitive::Type type) {
+ if (input->IsPhi()) {
+ return input->GetType() == HVecOperation::kSIMDType; // carries SIMD
+ }
DCHECK(input->IsVecOperation());
Primitive::Type input_type = input->AsVecOperation()->GetPackedType();
switch (input_type) {
@@ -265,27 +271,77 @@
DISALLOW_COPY_AND_ASSIGN(HVecReplicateScalar);
};
-// Sum-reduces the given vector into a shorter vector (m < n) or scalar (m = 1),
-// viz. sum-reduce[ x1, .. , xn ] = [ y1, .., ym ], where yi = sum_j x_j.
-class HVecSumReduce FINAL : public HVecUnaryOperation {
- HVecSumReduce(ArenaAllocator* arena,
- HInstruction* input,
- Primitive::Type packed_type,
- size_t vector_length,
- uint32_t dex_pc = kNoDexPc)
+// Extracts a particular scalar from the given vector,
+// viz. extract[ x1, .. , xn ] = x_i.
+//
+// TODO: for now only i == 1 case supported.
+class HVecExtractScalar FINAL : public HVecUnaryOperation {
+ public:
+ HVecExtractScalar(ArenaAllocator* arena,
+ HInstruction* input,
+ Primitive::Type packed_type,
+ size_t vector_length,
+ size_t index,
+ uint32_t dex_pc = kNoDexPc)
: HVecUnaryOperation(arena, input, packed_type, vector_length, dex_pc) {
DCHECK(HasConsistentPackedTypes(input, packed_type));
+ DCHECK_LT(index, vector_length);
+ DCHECK_EQ(index, 0u);
+ }
+
+ // Yields a single component in the vector.
+ Primitive::Type GetType() const OVERRIDE {
+ return GetPackedType();
+ }
+
+ // An extract needs to stay in place, since SIMD registers are not
+ // kept alive across vector loop boundaries (yet).
+ bool CanBeMoved() const OVERRIDE { return false; }
+
+ DECLARE_INSTRUCTION(VecExtractScalar);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HVecExtractScalar);
+};
+
+// Reduces the given vector into the first element as sum/min/max,
+// viz. sum-reduce[ x1, .. , xn ] = [ y, ---- ], where y = sum xi
+// and the "-" denotes "don't care" (implementation dependent).
+class HVecReduce FINAL : public HVecUnaryOperation {
+ public:
+ enum ReductionKind {
+ kSum = 1,
+ kMin = 2,
+ kMax = 3
+ };
+
+ HVecReduce(ArenaAllocator* arena,
+ HInstruction* input,
+ Primitive::Type packed_type,
+ size_t vector_length,
+ ReductionKind kind,
+ uint32_t dex_pc = kNoDexPc)
+ : HVecUnaryOperation(arena, input, packed_type, vector_length, dex_pc),
+ kind_(kind) {
+ DCHECK(HasConsistentPackedTypes(input, packed_type));
}
- // TODO: probably integral promotion
- Primitive::Type GetType() const OVERRIDE { return GetPackedType(); }
+ ReductionKind GetKind() const { return kind_; }
bool CanBeMoved() const OVERRIDE { return true; }
- DECLARE_INSTRUCTION(VecSumReduce);
+ bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+ DCHECK(other->IsVecReduce());
+ const HVecReduce* o = other->AsVecReduce();
+ return HVecOperation::InstructionDataEquals(o) && GetKind() == o->GetKind();
+ }
+
+ DECLARE_INSTRUCTION(VecReduce);
private:
- DISALLOW_COPY_AND_ASSIGN(HVecSumReduce);
+ const ReductionKind kind_;
+
+ DISALLOW_COPY_AND_ASSIGN(HVecReduce);
};
// Converts every component in the vector,
@@ -754,20 +810,23 @@
//
// Assigns the given scalar elements to a vector,
-// viz. set( array(x1, .., xn) ) = [ x1, .. , xn ].
+// viz. set( array(x1, .., xn) ) = [ x1, .. , xn ] if n == m,
+// set( array(x1, .., xm) ) = [ x1, .. , xm, 0, .., 0 ] if m < n.
class HVecSetScalars FINAL : public HVecOperation {
+ public:
HVecSetScalars(ArenaAllocator* arena,
HInstruction** scalars, // array
Primitive::Type packed_type,
size_t vector_length,
+ size_t number_of_scalars,
uint32_t dex_pc = kNoDexPc)
: HVecOperation(arena,
packed_type,
SideEffects::None(),
- /* number_of_inputs */ vector_length,
+ number_of_scalars,
vector_length,
dex_pc) {
- for (size_t i = 0; i < vector_length; i++) {
+ for (size_t i = 0; i < number_of_scalars; i++) {
DCHECK(!scalars[i]->IsVecOperation());
SetRawInputAt(0, scalars[i]);
}
diff --git a/compiler/optimizing/nodes_vector_test.cc b/compiler/optimizing/nodes_vector_test.cc
index 0238ea4..5a56a2c 100644
--- a/compiler/optimizing/nodes_vector_test.cc
+++ b/compiler/optimizing/nodes_vector_test.cc
@@ -332,4 +332,32 @@
EXPECT_FALSE(v1->Equals(v3)); // different vector lengths
}
+TEST_F(NodesVectorTest, VectorKindMattersOnReduce) {
+ HVecOperation* v0 = new (&allocator_)
+ HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 4);
+
+ HVecReduce* v1 = new (&allocator_) HVecReduce(
+ &allocator_, v0, Primitive::kPrimInt, 4, HVecReduce::kSum);
+ HVecReduce* v2 = new (&allocator_) HVecReduce(
+ &allocator_, v0, Primitive::kPrimInt, 4, HVecReduce::kMin);
+ HVecReduce* v3 = new (&allocator_) HVecReduce(
+ &allocator_, v0, Primitive::kPrimInt, 4, HVecReduce::kMax);
+
+ EXPECT_FALSE(v0->CanBeMoved());
+ EXPECT_TRUE(v1->CanBeMoved());
+ EXPECT_TRUE(v2->CanBeMoved());
+ EXPECT_TRUE(v3->CanBeMoved());
+
+ EXPECT_EQ(HVecReduce::kSum, v1->GetKind());
+ EXPECT_EQ(HVecReduce::kMin, v2->GetKind());
+ EXPECT_EQ(HVecReduce::kMax, v3->GetKind());
+
+ EXPECT_TRUE(v1->Equals(v1));
+ EXPECT_TRUE(v2->Equals(v2));
+ EXPECT_TRUE(v3->Equals(v3));
+
+ EXPECT_FALSE(v1->Equals(v2)); // different kinds
+ EXPECT_FALSE(v1->Equals(v3));
+}
+
} // namespace art
diff --git a/compiler/optimizing/scheduler_arm64.cc b/compiler/optimizing/scheduler_arm64.cc
index 510619f..1d9d28a 100644
--- a/compiler/optimizing/scheduler_arm64.cc
+++ b/compiler/optimizing/scheduler_arm64.cc
@@ -215,12 +215,12 @@
last_visited_latency_ = kArm64SIMDReplicateOpLatency;
}
-void SchedulingLatencyVisitorARM64::VisitVecSetScalars(HVecSetScalars* instr) {
- LOG(FATAL) << "Unsupported SIMD instruction " << instr->GetId();
+void SchedulingLatencyVisitorARM64::VisitVecExtractScalar(HVecExtractScalar* instr) {
+ HandleSimpleArithmeticSIMD(instr);
}
-void SchedulingLatencyVisitorARM64::VisitVecSumReduce(HVecSumReduce* instr) {
- LOG(FATAL) << "Unsupported SIMD instruction " << instr->GetId();
+void SchedulingLatencyVisitorARM64::VisitVecReduce(HVecReduce* instr) {
+ HandleSimpleArithmeticSIMD(instr);
}
void SchedulingLatencyVisitorARM64::VisitVecCnv(HVecCnv* instr ATTRIBUTE_UNUSED) {
@@ -283,8 +283,8 @@
last_visited_latency_ = kArm64SIMDIntegerOpLatency;
}
-void SchedulingLatencyVisitorARM64::VisitVecAndNot(HVecAndNot* instr) {
- LOG(FATAL) << "Unsupported SIMD instruction " << instr->GetId();
+void SchedulingLatencyVisitorARM64::VisitVecAndNot(HVecAndNot* instr ATTRIBUTE_UNUSED) {
+ last_visited_latency_ = kArm64SIMDIntegerOpLatency;
}
void SchedulingLatencyVisitorARM64::VisitVecOr(HVecOr* instr ATTRIBUTE_UNUSED) {
@@ -307,6 +307,10 @@
HandleSimpleArithmeticSIMD(instr);
}
+void SchedulingLatencyVisitorARM64::VisitVecSetScalars(HVecSetScalars* instr) {
+ HandleSimpleArithmeticSIMD(instr);
+}
+
void SchedulingLatencyVisitorARM64::VisitVecMultiplyAccumulate(
HVecMultiplyAccumulate* instr ATTRIBUTE_UNUSED) {
last_visited_latency_ = kArm64SIMDMulIntegerLatency;
diff --git a/compiler/optimizing/scheduler_arm64.h b/compiler/optimizing/scheduler_arm64.h
index 63d5b7d..e1a80ec 100644
--- a/compiler/optimizing/scheduler_arm64.h
+++ b/compiler/optimizing/scheduler_arm64.h
@@ -83,8 +83,8 @@
M(SuspendCheck , unused) \
M(TypeConversion , unused) \
M(VecReplicateScalar , unused) \
- M(VecSetScalars , unused) \
- M(VecSumReduce , unused) \
+ M(VecExtractScalar , unused) \
+ M(VecReduce , unused) \
M(VecCnv , unused) \
M(VecNeg , unused) \
M(VecAbs , unused) \
@@ -103,6 +103,7 @@
M(VecShl , unused) \
M(VecShr , unused) \
M(VecUShr , unused) \
+ M(VecSetScalars , unused) \
M(VecMultiplyAccumulate, unused) \
M(VecLoad , unused) \
M(VecStore , unused)
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index 46c5f58..04c2fcd 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -59,7 +59,7 @@
size_t class_frequency = 1) {
size_t method_counter = 0;
size_t class_counter = 0;
- for (std::string dex : GetLibCoreDexFileNames()) {
+ for (const std::string& dex : GetLibCoreDexFileNames()) {
std::vector<std::unique_ptr<const DexFile>> dex_files;
std::string error_msg;
CHECK(DexFile::Open(dex.c_str(), dex, /*verify_checksum*/ false, &error_msg, &dex_files))
@@ -158,9 +158,6 @@
argv.push_back("--host");
}
- ScratchFile file;
- const std::string image_prefix = file.GetFilename();
-
argv.push_back("--image=" + image_file_name_prefix + ".art");
argv.push_back("--oat-file=" + image_file_name_prefix + ".oat");
argv.push_back("--oat-location=" + image_file_name_prefix + ".oat");
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index dbb9dfc..db70e78 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -89,13 +89,31 @@
return status;
}
- void GenerateOdexForTest(const std::string& dex_location,
- const std::string& odex_location,
- CompilerFilter::Filter filter,
- const std::vector<std::string>& extra_args = {},
- bool expect_success = true,
- bool use_fd = false,
- std::function<void(const OatFile&)> check_oat = [](const OatFile&) {}) {
+ void GenerateOdexForTest(
+ const std::string& dex_location,
+ const std::string& odex_location,
+ CompilerFilter::Filter filter,
+ const std::vector<std::string>& extra_args = {},
+ bool expect_success = true,
+ bool use_fd = false) {
+ GenerateOdexForTest(dex_location,
+ odex_location,
+ filter,
+ extra_args,
+ expect_success,
+ use_fd,
+ [](const OatFile&) {});
+ }
+
+ template <typename T>
+ void GenerateOdexForTest(
+ const std::string& dex_location,
+ const std::string& odex_location,
+ CompilerFilter::Filter filter,
+ const std::vector<std::string>& extra_args,
+ bool expect_success,
+ bool use_fd,
+ T check_oat) {
std::string error_msg;
int status = GenerateOdexForTestWithStatus({dex_location},
odex_location,
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index 1658967..336eb5f 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -380,7 +380,7 @@
// Runs DexFileLayout test.
bool DexFileLayoutExec(std::string* error_msg) {
ScratchFile tmp_file;
- std::string tmp_name = tmp_file.GetFilename();
+ const std::string& tmp_name = tmp_file.GetFilename();
size_t tmp_last_slash = tmp_name.rfind('/');
std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1);
@@ -415,7 +415,7 @@
// for behavior consistency.
bool DexFileLayoutFixedPointExec(std::string* error_msg) {
ScratchFile tmp_file;
- std::string tmp_name = tmp_file.GetFilename();
+ const std::string& tmp_name = tmp_file.GetFilename();
size_t tmp_last_slash = tmp_name.rfind('/');
std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1);
@@ -481,7 +481,7 @@
// Runs UnreferencedCatchHandlerTest & Unreferenced0SizeCatchHandlerTest.
bool UnreferencedCatchHandlerExec(std::string* error_msg, const char* filename) {
ScratchFile tmp_file;
- std::string tmp_name = tmp_file.GetFilename();
+ const std::string& tmp_name = tmp_file.GetFilename();
size_t tmp_last_slash = tmp_name.rfind('/');
std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1);
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index af77072..6c0d492 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -183,22 +183,27 @@
}
static jvmtiError GetOwnedMonitorInfo(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint* owned_monitor_count_ptr ATTRIBUTE_UNUSED,
- jobject** owned_monitors_ptr ATTRIBUTE_UNUSED) {
+ jthread thread,
+ jint* owned_monitor_count_ptr,
+ jobject** owned_monitors_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_get_owned_monitor_info);
- return ERR(NOT_IMPLEMENTED);
+ return StackUtil::GetOwnedMonitorInfo(env,
+ thread,
+ owned_monitor_count_ptr,
+ owned_monitors_ptr);
}
- static jvmtiError GetOwnedMonitorStackDepthInfo(
- jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint* monitor_info_count_ptr ATTRIBUTE_UNUSED,
- jvmtiMonitorStackDepthInfo** monitor_info_ptr ATTRIBUTE_UNUSED) {
+ static jvmtiError GetOwnedMonitorStackDepthInfo(jvmtiEnv* env,
+ jthread thread,
+ jint* monitor_info_count_ptr,
+ jvmtiMonitorStackDepthInfo** monitor_info_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_get_owned_monitor_stack_depth_info);
- return ERR(NOT_IMPLEMENTED);
+ return StackUtil::GetOwnedMonitorStackDepthInfo(env,
+ thread,
+ monitor_info_count_ptr,
+ monitor_info_ptr);
}
static jvmtiError GetCurrentContendedMonitor(jvmtiEnv* env,
@@ -313,12 +318,10 @@
return StackUtil::GetFrameLocation(env, thread, depth, method_ptr, location_ptr);
}
- static jvmtiError NotifyFramePop(jvmtiEnv* env,
- jthread thread ATTRIBUTE_UNUSED,
- jint depth ATTRIBUTE_UNUSED) {
+ static jvmtiError NotifyFramePop(jvmtiEnv* env, jthread thread, jint depth) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_generate_frame_pop_events);
- return ERR(NOT_IMPLEMENTED);
+ return StackUtil::NotifyFramePop(env, thread, depth);
}
static jvmtiError ForceEarlyReturnObject(jvmtiEnv* env,
diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h
index 12f4cab..d74e25b 100644
--- a/openjdkjvmti/art_jvmti.h
+++ b/openjdkjvmti/art_jvmti.h
@@ -52,6 +52,7 @@
namespace art {
class ArtField;
class ArtMethod;
+class ShadowFrame;
} // namespace art
namespace openjdkjvmti {
@@ -81,6 +82,7 @@
// Set of breakpoints is unique to each jvmtiEnv.
std::unordered_set<Breakpoint> breakpoints;
+ std::unordered_set<const art::ShadowFrame*> notify_frames;
ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler);
@@ -222,7 +224,7 @@
.can_generate_field_access_events = 1,
.can_get_bytecodes = 1,
.can_get_synthetic_attribute = 1,
- .can_get_owned_monitor_info = 0,
+ .can_get_owned_monitor_info = 1,
.can_get_current_contended_monitor = 0,
.can_get_monitor_info = 0,
.can_pop_frame = 0,
@@ -235,7 +237,7 @@
.can_maintain_original_method_order = 1,
.can_generate_single_step_events = 1,
.can_generate_exception_events = 0,
- .can_generate_frame_pop_events = 0,
+ .can_generate_frame_pop_events = 1,
.can_generate_breakpoint_events = 1,
.can_suspend = 1,
.can_redefine_any_class = 0,
@@ -251,7 +253,7 @@
.can_generate_garbage_collection_events = 1,
.can_generate_object_free_events = 1,
.can_force_early_return = 0,
- .can_get_owned_monitor_stack_depth_info = 0,
+ .can_get_owned_monitor_stack_depth_info = 1,
.can_get_constant_pool = 0,
.can_set_native_method_prefix = 0,
.can_retransform_classes = 1,
diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h
index 32dba3e..31edc73 100644
--- a/openjdkjvmti/events-inl.h
+++ b/openjdkjvmti/events-inl.h
@@ -244,6 +244,35 @@
}
}
+// Need to give custom specializations for FramePop since it needs to filter out which particular
+// agents get the event. This specialization gets an extra argument so we can determine which (if
+// any) environments have the frame pop.
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kFramePop>(
+ art::Thread* thread,
+ JNIEnv* jnienv,
+ jthread jni_thread,
+ jmethodID jmethod,
+ jboolean is_exception,
+ const art::ShadowFrame* frame) const {
+ for (ArtJvmTiEnv* env : envs) {
+ // Search for the frame. Do this before checking if we need to send the event so that we don't
+ // have to deal with use-after-free or the frames being reallocated later.
+ if (env != nullptr && env->notify_frames.erase(frame) != 0) {
+ if (ShouldDispatch<ArtJvmtiEvent::kFramePop>(env, thread)) {
+ // We temporarily clear any pending exceptions so the event can call back into java code.
+ ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred());
+ jnienv->ExceptionClear();
+ auto callback = impl::GetCallback<ArtJvmtiEvent::kFramePop>(env);
+ (*callback)(env, jnienv, jni_thread, jmethod, is_exception);
+ if (thr.get() != nullptr && !jnienv->ExceptionCheck()) {
+ jnienv->Throw(thr.get());
+ }
+ }
+ }
+ }
+}
+
// Need to give custom specializations for FieldAccess and FieldModification since they need to
// filter out which particular fields agents want to get notified on.
// TODO The spec allows us to do shortcuts like only allow one agent to ever set these watches. This
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index 73e6881..acef682 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -538,6 +538,20 @@
}
}
+ void WatchedFramePop(art::Thread* self, const art::ShadowFrame& frame)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE {
+ if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kFramePop)) {
+ art::JNIEnvExt* jnienv = self->GetJniEnv();
+ jboolean is_exception_pending = self->IsExceptionPending();
+ RunEventCallback<ArtJvmtiEvent::kFramePop>(
+ self,
+ jnienv,
+ art::jni::EncodeArtMethod(frame.GetMethod()),
+ is_exception_pending,
+ &frame);
+ }
+ }
+
// Call-back when an exception is thrown.
void ExceptionThrown(art::Thread* self ATTRIBUTE_UNUSED,
art::Handle<art::mirror::Throwable> exception_object ATTRIBUTE_UNUSED)
@@ -582,6 +596,8 @@
case ArtJvmtiEvent::kBreakpoint:
case ArtJvmtiEvent::kSingleStep:
return art::instrumentation::Instrumentation::kDexPcMoved;
+ case ArtJvmtiEvent::kFramePop:
+ return art::instrumentation::Instrumentation::kWatchedFramePop;
default:
LOG(FATAL) << "Unknown event ";
return 0;
@@ -648,6 +664,15 @@
}
return;
}
+ // FramePop can never be disabled once it's been turned on since we would either need to deal
+ // with dangling pointers or have missed events.
+ case ArtJvmtiEvent::kFramePop:
+ if (!enable || (enable && frame_pop_enabled)) {
+ break;
+ } else {
+ SetupTraceListener(method_trace_listener_.get(), event, enable);
+ break;
+ }
case ArtJvmtiEvent::kMethodEntry:
case ArtJvmtiEvent::kMethodExit:
case ArtJvmtiEvent::kFieldAccess:
diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h
index 3d05fa1..b49e80c 100644
--- a/openjdkjvmti/events.h
+++ b/openjdkjvmti/events.h
@@ -223,6 +223,11 @@
std::unique_ptr<JvmtiAllocationListener> alloc_listener_;
std::unique_ptr<JvmtiGcPauseListener> gc_pause_listener_;
std::unique_ptr<JvmtiMethodTraceListener> method_trace_listener_;
+
+ // True if frame pop has ever been enabled. Since we store pointers to stack frames we need to
+ // continue to listen to this event even if it has been disabled.
+ // TODO We could remove the listeners once all jvmtiEnvs have drained their shadow-frame vectors.
+ bool frame_pop_enabled;
};
} // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index 35f2f0c..f99b167 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -51,6 +51,7 @@
#include "stack.h"
#include "thread-current-inl.h"
#include "thread_list.h"
+#include "ti_stack.h"
#include "ti_thread.h"
#include "ti_phase.h"
@@ -533,39 +534,6 @@
return IsMethodT(env, m, test, is_synthetic_ptr);
}
-struct FindFrameAtDepthVisitor : art::StackVisitor {
- public:
- FindFrameAtDepthVisitor(art::Thread* target, art::Context* ctx, jint depth)
- REQUIRES_SHARED(art::Locks::mutator_lock_)
- : art::StackVisitor(target, ctx, art::StackVisitor::StackWalkKind::kIncludeInlinedFrames),
- found_frame_(false),
- cnt_(0),
- depth_(static_cast<size_t>(depth)) { }
-
- bool FoundFrame() {
- return found_frame_;
- }
-
- bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
- if (GetMethod()->IsRuntimeMethod()) {
- return true;
- }
- if (cnt_ == depth_) {
- // We found our frame, exit.
- found_frame_ = true;
- return false;
- } else {
- cnt_++;
- return true;
- }
- }
-
- private:
- bool found_frame_;
- size_t cnt_;
- size_t depth_;
-};
-
class CommonLocalVariableClosure : public art::Closure {
public:
CommonLocalVariableClosure(art::Thread* caller,
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index c679d73..1b4e910 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -1364,7 +1364,6 @@
}
void Redefiner::ClassRedefinition::UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
- art::ObjPtr<art::mirror::DexCache> new_dex_cache,
const art::DexFile::ClassDef& class_def) {
art::ClassLinker* linker = driver_->runtime_->GetClassLinker();
art::PointerSize image_pointer_size = linker->GetImagePointerSize();
@@ -1396,7 +1395,6 @@
method.SetDexMethodIndex(dex_method_idx);
linker->SetEntryPointsToInterpreter(&method);
method.SetCodeItemOffset(dex_file_->FindCodeItemOffset(class_def, dex_method_idx));
- method.SetDexCacheResolvedMethods(new_dex_cache->GetResolvedMethods(), image_pointer_size);
// Clear all the intrinsics related flags.
method.ClearAccessFlags(art::kAccIntrinsic | (~art::kAccFlagsNotUsedByIntrinsic));
// Notify the jit that this method is redefined.
@@ -1433,7 +1431,7 @@
art::ObjPtr<art::mirror::Object> original_dex_file) {
DCHECK_EQ(dex_file_->NumClassDefs(), 1u);
const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(0);
- UpdateMethods(mclass, new_dex_cache, class_def);
+ UpdateMethods(mclass, class_def);
UpdateFields(mclass);
// Update the class fields.
diff --git a/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h
index 984f922..528563b 100644
--- a/openjdkjvmti/ti_redefine.h
+++ b/openjdkjvmti/ti_redefine.h
@@ -184,7 +184,6 @@
REQUIRES(art::Locks::mutator_lock_);
void UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
- art::ObjPtr<art::mirror::DexCache> new_dex_cache,
const art::DexFile::ClassDef& class_def)
REQUIRES(art::Locks::mutator_lock_);
diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc
index ff2de8d..e165187 100644
--- a/openjdkjvmti/ti_stack.cc
+++ b/openjdkjvmti/ti_stack.cc
@@ -37,6 +37,7 @@
#include <vector>
#include "art_field-inl.h"
+#include "art_method-inl.h"
#include "art_jvmti.h"
#include "art_method-inl.h"
#include "barrier.h"
@@ -45,6 +46,7 @@
#include "base/mutex.h"
#include "dex_file.h"
#include "dex_file_annotations.h"
+#include "gc_root.h"
#include "handle_scope-inl.h"
#include "jni_env_ext.h"
#include "jni_internal.h"
@@ -53,9 +55,11 @@
#include "nativehelper/ScopedLocalRef.h"
#include "scoped_thread_state_change-inl.h"
#include "stack.h"
+#include "ti_thread.h"
#include "thread-current-inl.h"
#include "thread_list.h"
#include "thread_pool.h"
+#include "ti_thread.h"
#include "well_known_classes.h"
namespace openjdkjvmti {
@@ -824,4 +828,239 @@
return ERR(NONE);
}
+struct MonitorVisitor : public art::StackVisitor, public art::SingleRootVisitor {
+ // We need a context because VisitLocks needs it retrieve the monitor objects.
+ explicit MonitorVisitor(art::Thread* thread)
+ REQUIRES_SHARED(art::Locks::mutator_lock_)
+ : art::StackVisitor(thread,
+ art::Context::Create(),
+ art::StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+ hs(art::Thread::Current()),
+ current_stack_depth(0) {}
+
+ ~MonitorVisitor() {
+ delete context_;
+ }
+
+ bool VisitFrame() OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current());
+ if (!GetMethod()->IsRuntimeMethod()) {
+ art::Monitor::VisitLocks(this, AppendOwnedMonitors, this);
+ ++current_stack_depth;
+ }
+ return true;
+ }
+
+ static void AppendOwnedMonitors(art::mirror::Object* owned_monitor, void* arg)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current());
+ MonitorVisitor* visitor = reinterpret_cast<MonitorVisitor*>(arg);
+ art::ObjPtr<art::mirror::Object> mon(owned_monitor);
+ // Filter out duplicates.
+ for (const art::Handle<art::mirror::Object>& monitor : visitor->monitors) {
+ if (monitor.Get() == mon.Ptr()) {
+ return;
+ }
+ }
+ visitor->monitors.push_back(visitor->hs.NewHandle(mon));
+ visitor->stack_depths.push_back(visitor->current_stack_depth);
+ }
+
+ void VisitRoot(art::mirror::Object* obj, const art::RootInfo& info ATTRIBUTE_UNUSED)
+ OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ for (const art::Handle<art::mirror::Object>& m : monitors) {
+ if (m.Get() == obj) {
+ return;
+ }
+ }
+ monitors.push_back(hs.NewHandle(obj));
+ stack_depths.push_back(-1);
+ }
+
+ art::VariableSizedHandleScope hs;
+ jint current_stack_depth;
+ std::vector<art::Handle<art::mirror::Object>> monitors;
+ std::vector<jint> stack_depths;
+};
+
+template<typename Fn>
+struct MonitorInfoClosure : public art::Closure {
+ public:
+ MonitorInfoClosure(art::ScopedObjectAccess& soa, Fn handle_results)
+ : soa_(soa), err_(OK), handle_results_(handle_results) {}
+
+ void Run(art::Thread* target) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current());
+ // Find the monitors on the stack.
+ MonitorVisitor visitor(target);
+ visitor.WalkStack(/* include_transitions */ false);
+ // Find any other monitors, including ones acquired in native code.
+ art::RootInfo root_info(art::kRootVMInternal);
+ target->GetJniEnv()->monitors.VisitRoots(&visitor, root_info);
+ err_ = handle_results_(soa_, visitor);
+ }
+
+ jvmtiError GetError() {
+ return err_;
+ }
+
+ private:
+ art::ScopedObjectAccess& soa_;
+ jvmtiError err_;
+ Fn handle_results_;
+};
+
+
+template <typename Fn>
+static jvmtiError GetOwnedMonitorInfoCommon(jthread thread, Fn handle_results) {
+ art::Thread* self = art::Thread::Current();
+ art::ScopedObjectAccess soa(self);
+ MonitorInfoClosure<Fn> closure(soa, handle_results);
+ bool called_method = false;
+ {
+ art::MutexLock mu(self, *art::Locks::thread_list_lock_);
+ art::Thread* target = ThreadUtil::GetNativeThread(thread, soa);
+ if (target == nullptr && thread == nullptr) {
+ return ERR(INVALID_THREAD);
+ }
+ if (target == nullptr) {
+ return ERR(THREAD_NOT_ALIVE);
+ }
+ if (target != self) {
+ called_method = true;
+ if (!target->RequestSynchronousCheckpoint(&closure)) {
+ return ERR(THREAD_NOT_ALIVE);
+ }
+ }
+ }
+ // Cannot call the closure on the current thread if we have thread_list_lock since we need to call
+ // into the verifier which can cause the current thread to suspend for gc. Suspending would be a
+ // bad thing to do if we hold the ThreadListLock. For other threads since we are running it on a
+ // checkpoint we are fine but if the thread is the current one we need to drop the mutex first.
+ if (!called_method) {
+ closure.Run(self);
+ }
+ return closure.GetError();
+}
+
+jvmtiError StackUtil::GetOwnedMonitorStackDepthInfo(jvmtiEnv* env,
+ jthread thread,
+ jint* info_cnt,
+ jvmtiMonitorStackDepthInfo** info_ptr) {
+ if (info_cnt == nullptr || info_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+ auto handle_fun = [&] (art::ScopedObjectAccess& soa, MonitorVisitor& visitor)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ auto nbytes = sizeof(jvmtiMonitorStackDepthInfo) * visitor.monitors.size();
+ jvmtiError err = env->Allocate(nbytes, reinterpret_cast<unsigned char**>(info_ptr));
+ if (err != OK) {
+ return err;
+ }
+ *info_cnt = visitor.monitors.size();
+ for (size_t i = 0; i < visitor.monitors.size(); i++) {
+ (*info_ptr)[i] = {
+ soa.Env()->AddLocalReference<jobject>(visitor.monitors[i].Get()),
+ visitor.stack_depths[i]
+ };
+ }
+ return OK;
+ };
+ return GetOwnedMonitorInfoCommon(thread, handle_fun);
+}
+
+jvmtiError StackUtil::GetOwnedMonitorInfo(jvmtiEnv* env,
+ jthread thread,
+ jint* owned_monitor_count_ptr,
+ jobject** owned_monitors_ptr) {
+ if (owned_monitors_ptr == nullptr || owned_monitors_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+ auto handle_fun = [&] (art::ScopedObjectAccess& soa, MonitorVisitor& visitor)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ auto nbytes = sizeof(jobject) * visitor.monitors.size();
+ jvmtiError err = env->Allocate(nbytes, reinterpret_cast<unsigned char**>(owned_monitors_ptr));
+ if (err != OK) {
+ return err;
+ }
+ *owned_monitor_count_ptr = visitor.monitors.size();
+ for (size_t i = 0; i < visitor.monitors.size(); i++) {
+ (*owned_monitors_ptr)[i] =
+ soa.Env()->AddLocalReference<jobject>(visitor.monitors[i].Get());
+ }
+ return OK;
+ };
+ return GetOwnedMonitorInfoCommon(thread, handle_fun);
+}
+
+jvmtiError StackUtil::NotifyFramePop(jvmtiEnv* env, jthread thread, jint depth) {
+ if (depth < 0) {
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+ ArtJvmTiEnv* tienv = ArtJvmTiEnv::AsArtJvmTiEnv(env);
+ art::Thread* self = art::Thread::Current();
+ art::Thread* target;
+ do {
+ ThreadUtil::SuspendCheck(self);
+ art::MutexLock ucsl_mu(self, *art::Locks::user_code_suspension_lock_);
+ // Make sure we won't be suspended in the middle of holding the thread_suspend_count_lock_ by a
+ // user-code suspension. We retry and do another SuspendCheck to clear this.
+ if (ThreadUtil::WouldSuspendForUserCodeLocked(self)) {
+ continue;
+ }
+ // From now on we know we cannot get suspended by user-code.
+ // NB This does a SuspendCheck (during thread state change) so we need to make sure we don't
+ // have the 'suspend_lock' locked here.
+ art::ScopedObjectAccess soa(self);
+ art::MutexLock tll_mu(self, *art::Locks::thread_list_lock_);
+ target = ThreadUtil::GetNativeThread(thread, soa);
+ if (target == nullptr) {
+ return ERR(THREAD_NOT_ALIVE);
+ } else if (target != self) {
+ // TODO This is part of the spec but we could easily avoid needing to do it. We would just put
+ // all the logic into a sync-checkpoint.
+ art::MutexLock tscl_mu(self, *art::Locks::thread_suspend_count_lock_);
+ if (target->GetUserCodeSuspendCount() == 0) {
+ return ERR(THREAD_NOT_SUSPENDED);
+ }
+ }
+ // We hold the user_code_suspension_lock_ so the target thread is staying suspended until we are
+ // done (unless it's 'self' in which case we don't care since we aren't going to be returning).
+ // TODO We could implement this using a synchronous checkpoint and not bother with any of the
+ // suspension stuff. The spec does specifically say to return THREAD_NOT_SUSPENDED though.
+ // Find the requested stack frame.
+ std::unique_ptr<art::Context> context(art::Context::Create());
+ FindFrameAtDepthVisitor visitor(target, context.get(), depth);
+ visitor.WalkStack();
+ if (!visitor.FoundFrame()) {
+ return ERR(NO_MORE_FRAMES);
+ }
+ art::ArtMethod* method = visitor.GetMethod();
+ if (method->IsNative()) {
+ return ERR(OPAQUE_FRAME);
+ }
+ // From here we are sure to succeed.
+ bool needs_instrument = false;
+ // Get/create a shadow frame
+ art::ShadowFrame* shadow_frame = visitor.GetCurrentShadowFrame();
+ if (shadow_frame == nullptr) {
+ needs_instrument = true;
+ const size_t frame_id = visitor.GetFrameId();
+ const uint16_t num_regs = method->GetCodeItem()->registers_size_;
+ shadow_frame = target->FindOrCreateDebuggerShadowFrame(frame_id,
+ num_regs,
+ method,
+ visitor.GetDexPc());
+ }
+ // Mark shadow frame as needs_notify_pop_
+ shadow_frame->SetNotifyPop(true);
+ tienv->notify_frames.insert(shadow_frame);
+ // Make sure can we will go to the interpreter and use the shadow frames.
+ if (needs_instrument) {
+ art::Runtime::Current()->GetInstrumentation()->InstrumentThreadStack(target);
+ }
+ return OK;
+ } while (true);
+}
+
} // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_stack.h b/openjdkjvmti/ti_stack.h
index 2e96b82..b41fa4b 100644
--- a/openjdkjvmti/ti_stack.h
+++ b/openjdkjvmti/ti_stack.h
@@ -35,7 +35,9 @@
#include "jni.h"
#include "jvmti.h"
+#include "art_method.h"
#include "base/mutex.h"
+#include "stack.h"
namespace openjdkjvmti {
@@ -67,6 +69,51 @@
const jthread* thread_list,
jint max_frame_count,
jvmtiStackInfo** stack_info_ptr);
+
+ static jvmtiError GetOwnedMonitorStackDepthInfo(jvmtiEnv* env,
+ jthread thread,
+ jint* info_cnt_ptr,
+ jvmtiMonitorStackDepthInfo** info_ptr);
+
+ static jvmtiError GetOwnedMonitorInfo(jvmtiEnv* env,
+ jthread thread,
+ jint* owned_monitor_count_ptr,
+ jobject** owned_monitors_ptr);
+
+ static jvmtiError NotifyFramePop(jvmtiEnv* env, jthread thread, jint depth);
+};
+
+struct FindFrameAtDepthVisitor : art::StackVisitor {
+ public:
+ FindFrameAtDepthVisitor(art::Thread* target, art::Context* ctx, jint depth)
+ REQUIRES_SHARED(art::Locks::mutator_lock_)
+ : art::StackVisitor(target, ctx, art::StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+ found_frame_(false),
+ cnt_(0),
+ depth_(static_cast<size_t>(depth)) { }
+
+ bool FoundFrame() {
+ return found_frame_;
+ }
+
+ bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
+ if (GetMethod()->IsRuntimeMethod()) {
+ return true;
+ }
+ if (cnt_ == depth_) {
+ // We found our frame, exit.
+ found_frame_ = true;
+ return false;
+ } else {
+ cnt_++;
+ return true;
+ }
+ }
+
+ private:
+ bool found_frame_;
+ size_t cnt_;
+ size_t depth_;
};
} // namespace openjdkjvmti
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index ed7623a..efa2969 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -686,8 +686,6 @@
// Just update the entry points if it looks like we should.
// TODO: sanity check all the pointers' values
copy->SetDeclaringClass(RelocatedAddressOfPointer(object->GetDeclaringClass()));
- copy->SetDexCacheResolvedMethods(
- RelocatedAddressOfPointer(object->GetDexCacheResolvedMethods(pointer_size)), pointer_size);
copy->SetEntryPointFromQuickCompiledCodePtrSize(RelocatedAddressOfPointer(
object->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size)), pointer_size);
// No special handling for IMT conflict table since all pointers are moved by the same offset.
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index c78d34e..8cbf8c3 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -207,7 +207,7 @@
return ExecAndReturnCode(argv_str, &error);
}
- bool CreateProfile(std::string profile_file_contents,
+ bool CreateProfile(const std::string& profile_file_contents,
const std::string& filename,
const std::string& dex_location) {
ScratchFile class_names_file;
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index cf2bfee..69c615d 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -1596,10 +1596,21 @@
.cfi_rel_offset r1, 0
.cfi_rel_offset r2, 4
ldr r4, [sp, #(2 * 4)] // Load referrer.
- ubfx r1, r12, #0, #METHOD_DEX_CACHE_HASH_BITS // Calculate DexCache method slot index.
- ldr r4, [r4, #ART_METHOD_DEX_CACHE_METHODS_OFFSET_32] // Load dex cache methods array
- add r4, r4, r1, lsl #(POINTER_SIZE_SHIFT + 1) // Load DexCache method slot address.
ldr r2, [r0, #ART_METHOD_JNI_OFFSET_32] // Load ImtConflictTable
+ // Load the declaring class (without read barrier) and access flags (for obsolete method check).
+ // The obsolete flag is set with suspended threads, so we do not need an acquire operation here.
+#if ART_METHOD_ACCESS_FLAGS_OFFSET != ART_METHOD_DECLARING_CLASS_OFFSET + 4
+#error "Expecting declaring class and access flags to be consecutive for LDRD."
+#endif
+ ldrd r0, r1, [r4, #ART_METHOD_DECLARING_CLASS_OFFSET]
+ // If the method is obsolete, just go through the dex cache miss slow path.
+ lsrs r1, #(ACC_OBSOLETE_METHOD_SHIFT + 1)
+ bcs .Limt_conflict_trampoline_dex_cache_miss
+ ldr r4, [r0, #MIRROR_CLASS_DEX_CACHE_OFFSET] // Load the DexCache (without read barrier).
+ UNPOISON_HEAP_REF r4
+ ubfx r1, r12, #0, #METHOD_DEX_CACHE_HASH_BITS // Calculate DexCache method slot index.
+ ldr r4, [r4, #MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET] // Load the resolved methods.
+ add r4, r4, r1, lsl #(POINTER_SIZE_SHIFT + 1) // Load DexCache method slot address.
// FIXME: Configure the build to use the faster code when appropriate.
// Currently we fall back to the slower version.
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 3d8ca40..802cf5e 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -2060,8 +2060,18 @@
.extern artLookupResolvedMethod
ENTRY art_quick_imt_conflict_trampoline
ldr xIP0, [sp, #0] // Load referrer
+ // Load the declaring class (without read barrier) and access flags (for obsolete method check).
+ // The obsolete flag is set with suspended threads, so we do not need an acquire operation here.
+#if ART_METHOD_ACCESS_FLAGS_OFFSET != ART_METHOD_DECLARING_CLASS_OFFSET + 4
+#error "Expecting declaring class and access flags to be consecutive for LDP."
+#endif
+ ldp wIP0, w15, [xIP0, #ART_METHOD_DECLARING_CLASS_OFFSET]
+ // If the method is obsolete, just go through the dex cache miss slow path.
+ tbnz x15, #ACC_OBSOLETE_METHOD_SHIFT, .Limt_conflict_trampoline_dex_cache_miss
+ ldr wIP0, [xIP0, #MIRROR_CLASS_DEX_CACHE_OFFSET] // Load the DexCache (without read barrier).
+ UNPOISON_HEAP_REF wIP0
ubfx x15, xIP1, #0, #METHOD_DEX_CACHE_HASH_BITS // Calculate DexCache method slot index.
- ldr xIP0, [xIP0, #ART_METHOD_DEX_CACHE_METHODS_OFFSET_64] // Load dex cache methods array
+ ldr xIP0, [xIP0, #MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET] // Load the resolved methods.
add xIP0, xIP0, x15, lsl #(POINTER_SIZE_SHIFT + 1) // Load DexCache method slot address.
// Relaxed atomic load x14:x15 from the dex cache slot.
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index bb82d58..b876353 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -2112,10 +2112,18 @@
SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY /* save_s4_thru_s8 */ 0
lw $t8, FRAME_SIZE_SAVE_REFS_AND_ARGS($sp) # $t8 = referrer.
+ // If the method is obsolete, just go through the dex cache miss slow path.
+ // The obsolete flag is set with suspended threads, so we do not need an acquire operation here.
+ lw $t9, ART_METHOD_ACCESS_FLAGS_OFFSET($t8) # $t9 = access flags.
+ sll $t9, $t9, 31 - ACC_OBSOLETE_METHOD_SHIFT # Move obsolete method bit to sign bit.
+ bltz $t9, .Limt_conflict_trampoline_dex_cache_miss
+ lw $t8, ART_METHOD_DECLARING_CLASS_OFFSET($t8) # $t8 = declaring class (no read barrier).
+ lw $t8, MIRROR_CLASS_DEX_CACHE_OFFSET($t8) # $t8 = dex cache (without read barrier).
+ UNPOISON_HEAP_REF $t8
la $t9, __atomic_load_8
addiu $sp, $sp, -ARG_SLOT_SIZE # Reserve argument slots on the stack.
.cfi_adjust_cfa_offset ARG_SLOT_SIZE
- lw $t8, ART_METHOD_DEX_CACHE_METHODS_OFFSET_32($t8) # $t8 = dex cache methods array.
+ lw $t8, MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET($t8) # $t8 = dex cache methods array.
move $s2, $t7 # $s2 = method index (callee-saved).
lw $s3, ART_METHOD_JNI_OFFSET_32($a0) # $s3 = ImtConflictTable (callee-saved).
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 7350c85..eeaae3c 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -2024,8 +2024,16 @@
SETUP_SAVE_REFS_AND_ARGS_FRAME_INTERNAL /* save_s4_thru_s8 */ 0
ld $t1, FRAME_SIZE_SAVE_REFS_AND_ARGS($sp) # $t1 = referrer.
+ // If the method is obsolete, just go through the dex cache miss slow path.
+ // The obsolete flag is set with suspended threads, so we do not need an acquire operation here.
+ lw $t9, ART_METHOD_ACCESS_FLAGS_OFFSET($t1) # $t9 = access flags.
+ sll $t9, $t9, 31 - ACC_OBSOLETE_METHOD_SHIFT # Move obsolete method bit to sign bit.
+ bltzc $t9, .Limt_conflict_trampoline_dex_cache_miss
+ lwu $t1, ART_METHOD_DECLARING_CLASS_OFFSET($t1) # $t1 = declaring class (no read barrier).
+ lwu $t1, MIRROR_CLASS_DEX_CACHE_OFFSET($t1) # $t1 = dex cache (without read barrier).
+ UNPOISON_HEAP_REF $t1
dla $t9, __atomic_load_16
- ld $t1, ART_METHOD_DEX_CACHE_METHODS_OFFSET_64($t1) # $t1 = dex cache methods array.
+ ld $t1, MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET($t1) # $t1 = dex cache methods array.
dext $s2, $t0, 0, 32 # $s2 = zero-extended method index
# (callee-saved).
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 4e5e93a..f08c7fe 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1787,7 +1787,14 @@
PUSH ESI
PUSH EDX
movl 16(%esp), %edi // Load referrer.
- movl ART_METHOD_DEX_CACHE_METHODS_OFFSET_32(%edi), %edi // Load dex cache methods array.
+ // If the method is obsolete, just go through the dex cache miss slow path.
+ // The obsolete flag is set with suspended threads, so we do not need an acquire operation here.
+ testl LITERAL(ACC_OBSOLETE_METHOD), ART_METHOD_ACCESS_FLAGS_OFFSET(%edi)
+ jnz .Limt_conflict_trampoline_dex_cache_miss
+ movl ART_METHOD_DECLARING_CLASS_OFFSET(%edi), %edi // Load declaring class (no read barrier).
+ movl MIRROR_CLASS_DEX_CACHE_OFFSET(%edi), %edi // Load the DexCache (without read barrier).
+ UNPOISON_HEAP_REF edi
+ movl MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET(%edi), %edi // Load the resolved methods.
pushl ART_METHOD_JNI_OFFSET_32(%eax) // Push ImtConflictTable.
CFI_ADJUST_CFA_OFFSET(4)
movd %xmm7, %eax // Get target method index stored in xmm7.
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 73e8610..b70abaa 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1646,7 +1646,14 @@
int3
#else
movq __SIZEOF_POINTER__(%rsp), %r10 // Load referrer.
- movq ART_METHOD_DEX_CACHE_METHODS_OFFSET_64(%r10), %r10 // Load dex cache methods array.
+ // If the method is obsolete, just go through the dex cache miss slow path.
+ // The obsolete flag is set with suspended threads, so we do not need an acquire operation here.
+ testl LITERAL(ACC_OBSOLETE_METHOD), ART_METHOD_ACCESS_FLAGS_OFFSET(%r10)
+ jnz .Limt_conflict_trampoline_dex_cache_miss
+ movl ART_METHOD_DECLARING_CLASS_OFFSET(%r10), %r10d // Load declaring class (no read barrier).
+ movl MIRROR_CLASS_DEX_CACHE_OFFSET(%r10), %r10d // Load the DexCache (without read barrier).
+ UNPOISON_HEAP_REF r10d
+ movq MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET(%r10), %r10 // Load the resolved methods.
mov %eax, %r11d // Remember method index in R11.
andl LITERAL(METHOD_DEX_CACHE_SIZE_MINUS_ONE), %eax // Calculate DexCache method slot index.
shll LITERAL(1), %eax // Multiply by 2 as entries have size 2 * __SIZEOF_POINTER__.
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 11f8253..1588920 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -102,58 +102,6 @@
return GetDexMethodIndexUnchecked();
}
-inline mirror::MethodDexCacheType* ArtMethod::GetDexCacheResolvedMethods(PointerSize pointer_size) {
- return GetNativePointer<mirror::MethodDexCacheType*>(DexCacheResolvedMethodsOffset(pointer_size),
- pointer_size);
-}
-
-inline ArtMethod* ArtMethod::GetDexCacheResolvedMethod(uint16_t method_index,
- PointerSize pointer_size) {
- // NOTE: Unchecked, i.e. not throwing AIOOB. We don't even know the length here
- // without accessing the DexCache and we don't want to do that in release build.
- DCHECK_LT(method_index, GetInterfaceMethodIfProxy(pointer_size)->GetDexFile()->NumMethodIds());
- uint32_t slot_idx = method_index % mirror::DexCache::kDexCacheMethodCacheSize;
- DCHECK_LT(slot_idx, GetInterfaceMethodIfProxy(pointer_size)->GetDexCache()->NumResolvedMethods());
- mirror::MethodDexCachePair pair = mirror::DexCache::GetNativePairPtrSize(
- GetDexCacheResolvedMethods(pointer_size), slot_idx, pointer_size);
- ArtMethod* method = pair.GetObjectForIndex(method_index);
- if (LIKELY(method != nullptr)) {
- auto* declaring_class = method->GetDeclaringClass();
- if (LIKELY(declaring_class == nullptr || !declaring_class->IsErroneous())) {
- return method;
- }
- }
- return nullptr;
-}
-
-inline void ArtMethod::SetDexCacheResolvedMethod(uint16_t method_index,
- ArtMethod* new_method,
- PointerSize pointer_size) {
- // NOTE: Unchecked, i.e. not throwing AIOOB. We don't even know the length here
- // without accessing the DexCache and we don't want to do that in release build.
- DCHECK_LT(method_index, GetInterfaceMethodIfProxy(pointer_size)->GetDexFile()->NumMethodIds());
- DCHECK(new_method == nullptr || new_method->GetDeclaringClass() != nullptr);
- uint32_t slot_idx = method_index % mirror::DexCache::kDexCacheMethodCacheSize;
- DCHECK_LT(slot_idx, GetInterfaceMethodIfProxy(pointer_size)->GetDexCache()->NumResolvedMethods());
- mirror::MethodDexCachePair pair(new_method, method_index);
- mirror::DexCache::SetNativePairPtrSize(
- GetDexCacheResolvedMethods(pointer_size), slot_idx, pair, pointer_size);
-}
-
-inline bool ArtMethod::HasDexCacheResolvedMethods(PointerSize pointer_size) {
- return GetDexCacheResolvedMethods(pointer_size) != nullptr;
-}
-
-inline bool ArtMethod::HasSameDexCacheResolvedMethods(ArtMethod* other, PointerSize pointer_size) {
- return GetDexCacheResolvedMethods(pointer_size) ==
- other->GetDexCacheResolvedMethods(pointer_size);
-}
-
-inline bool ArtMethod::HasSameDexCacheResolvedMethods(mirror::MethodDexCacheType* other_cache,
- PointerSize pointer_size) {
- return GetDexCacheResolvedMethods(pointer_size) == other_cache;
-}
-
inline ObjPtr<mirror::Class> ArtMethod::LookupResolvedClassFromTypeIndex(dex::TypeIndex type_idx) {
ObjPtr<mirror::DexCache> dex_cache = GetDexCache();
ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx);
@@ -403,13 +351,6 @@
return interface_method;
}
-inline void ArtMethod::SetDexCacheResolvedMethods(mirror::MethodDexCacheType* new_dex_cache_methods,
- PointerSize pointer_size) {
- SetNativePointer(DexCacheResolvedMethodsOffset(pointer_size),
- new_dex_cache_methods,
- pointer_size);
-}
-
inline dex::TypeIndex ArtMethod::GetReturnTypeIndex() {
DCHECK(!IsProxyMethod());
const DexFile* dex_file = GetDexFile();
@@ -489,18 +430,12 @@
}
template <typename Visitor>
-inline void ArtMethod::UpdateObjectsForImageRelocation(const Visitor& visitor,
- PointerSize pointer_size) {
+inline void ArtMethod::UpdateObjectsForImageRelocation(const Visitor& visitor) {
mirror::Class* old_class = GetDeclaringClassUnchecked<kWithoutReadBarrier>();
mirror::Class* new_class = visitor(old_class);
if (old_class != new_class) {
SetDeclaringClass(new_class);
}
- mirror::MethodDexCacheType* old_methods = GetDexCacheResolvedMethods(pointer_size);
- mirror::MethodDexCacheType* new_methods = visitor(old_methods);
- if (old_methods != new_methods) {
- SetDexCacheResolvedMethods(new_methods, pointer_size);
- }
}
template <ReadBarrierOption kReadBarrierOption, typename Visitor>
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 64988f2..2d67761 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -137,6 +137,10 @@
} while (!access_flags_.compare_exchange_weak(old_access_flags, new_access_flags));
}
+ static MemberOffset AccessFlagsOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(ArtMethod, access_flags_));
+ }
+
// Approximate what kind of method call would be used for this method.
InvokeType GetInvokeType() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -356,26 +360,6 @@
dex_method_index_ = new_idx;
}
- ALWAYS_INLINE mirror::MethodDexCacheType* GetDexCacheResolvedMethods(PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
- ALWAYS_INLINE ArtMethod* GetDexCacheResolvedMethod(uint16_t method_index,
- PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- ALWAYS_INLINE void SetDexCacheResolvedMethod(uint16_t method_index,
- ArtMethod* new_method,
- PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
- ALWAYS_INLINE void SetDexCacheResolvedMethods(mirror::MethodDexCacheType* new_dex_cache_methods,
- PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
- bool HasDexCacheResolvedMethods(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_);
- bool HasSameDexCacheResolvedMethods(ArtMethod* other, PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
- bool HasSameDexCacheResolvedMethods(mirror::MethodDexCacheType* other_cache,
- PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
// Lookup the Class* from the type index into this method's dex cache.
ObjPtr<mirror::Class> LookupResolvedClassFromTypeIndex(dex::TypeIndex type_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -427,12 +411,6 @@
void UnregisterNative() REQUIRES_SHARED(Locks::mutator_lock_);
- static MemberOffset DexCacheResolvedMethodsOffset(PointerSize pointer_size) {
- return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER(
- PtrSizedFields, dex_cache_resolved_methods_) / sizeof(void*)
- * static_cast<size_t>(pointer_size));
- }
-
static MemberOffset DataOffset(PointerSize pointer_size) {
return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER(
PtrSizedFields, data_) / sizeof(void*) * static_cast<size_t>(pointer_size));
@@ -686,8 +664,7 @@
// Update heap objects and non-entrypoint pointers by the passed in visitor for image relocation.
// Does not use read barrier.
template <typename Visitor>
- ALWAYS_INLINE void UpdateObjectsForImageRelocation(const Visitor& visitor,
- PointerSize pointer_size)
+ ALWAYS_INLINE void UpdateObjectsForImageRelocation(const Visitor& visitor)
REQUIRES_SHARED(Locks::mutator_lock_);
// Update entry points by passing them through the visitor.
@@ -728,9 +705,6 @@
// Must be the last fields in the method.
struct PtrSizedFields {
- // Short cuts to declaring_class_->dex_cache_ member for fast compiled code access.
- mirror::MethodDexCacheType* dex_cache_resolved_methods_;
-
// Depending on the method type, the data is
// - native method: pointer to the JNI function registered to this method
// or a function to resolve the JNI function,
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 44c0661..f4830e2 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -141,7 +141,7 @@
#define SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET (SHADOWFRAME_NUMBER_OF_VREGS_OFFSET + 10)
ADD_TEST_EQ(SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET,
static_cast<int32_t>(art::ShadowFrame::HotnessCountdownOffset()))
-#define SHADOWFRAME_VREGS_OFFSET (SHADOWFRAME_NUMBER_OF_VREGS_OFFSET + 12)
+#define SHADOWFRAME_VREGS_OFFSET (SHADOWFRAME_NUMBER_OF_VREGS_OFFSET + 16)
ADD_TEST_EQ(SHADOWFRAME_VREGS_OFFSET,
static_cast<int32_t>(art::ShadowFrame::VRegsOffset()))
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 439ecaf..d6f0030 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -186,7 +186,8 @@
// lookup in the context of the original method from where it steals the code.
// However, we delay the GetInterfaceMethodIfProxy() until needed.
DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor());
- ArtMethod* resolved_method = referrer->GetDexCacheResolvedMethod(method_idx, image_pointer_size_);
+ ArtMethod* resolved_method = referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedMethod(
+ method_idx, image_pointer_size_);
if (resolved_method == nullptr) {
return nullptr;
}
@@ -226,7 +227,8 @@
// However, we delay the GetInterfaceMethodIfProxy() until needed.
DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor());
Thread::PoisonObjectPointersIfDebug();
- ArtMethod* resolved_method = referrer->GetDexCacheResolvedMethod(method_idx, image_pointer_size_);
+ ArtMethod* resolved_method = referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedMethod(
+ method_idx, image_pointer_size_);
DCHECK(resolved_method == nullptr || !resolved_method->IsRuntimeMethod());
if (UNLIKELY(resolved_method == nullptr)) {
referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 051c0c2..1beb783 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1115,35 +1115,6 @@
return true;
}
-class FixupArtMethodArrayVisitor : public ArtMethodVisitor {
- public:
- explicit FixupArtMethodArrayVisitor(const ImageHeader& header) : header_(header) {}
-
- virtual void Visit(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
- const bool is_copied = method->IsCopied();
- mirror::MethodDexCacheType* resolved_methods =
- method->GetDexCacheResolvedMethods(kRuntimePointerSize);
- if (resolved_methods != nullptr) {
- bool in_image_space = false;
- if (kIsDebugBuild || is_copied) {
- in_image_space = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains(
- reinterpret_cast<const uint8_t*>(resolved_methods) - header_.GetImageBegin());
- }
- // Must be in image space for non-miranda method.
- DCHECK(is_copied || in_image_space)
- << resolved_methods << " is not in image starting at "
- << reinterpret_cast<void*>(header_.GetImageBegin());
- if (!is_copied || in_image_space) {
- method->SetDexCacheResolvedMethods(method->GetDexCache()->GetResolvedMethods(),
- kRuntimePointerSize);
- }
- }
- }
-
- private:
- const ImageHeader& header_;
-};
-
class VerifyDeclaringClassVisitor : public ArtMethodVisitor {
public:
VerifyDeclaringClassVisitor() REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_)
@@ -1492,12 +1463,6 @@
FixupInternVisitor fixup_intern_visitor;
bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_intern_visitor);
}
- if (*out_forward_dex_cache_array) {
- ScopedTrace timing("Fixup ArtMethod dex cache arrays");
- FixupArtMethodArrayVisitor visitor(header);
- header.VisitPackedArtMethods(&visitor, space->Begin(), kRuntimePointerSize);
- Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get());
- }
if (kVerifyArtMethodDeclaringClasses) {
ScopedTrace timing("Verify declaring classes");
ReaderMutexLock rmu(self, *Locks::heap_bitmap_lock_);
@@ -3444,8 +3409,6 @@
dst->SetDeclaringClass(klass.Get());
dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());
- dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods(), image_pointer_size_);
-
uint32_t access_flags = it.GetMethodAccessFlags();
if (UNLIKELY(strcmp("finalize", method_name) == 0)) {
@@ -4729,7 +4692,6 @@
// The proxy method doesn't have its own dex cache or dex file and so it steals those of its
// interface prototype. The exception to this are Constructors and the Class of the Proxy itself.
- CHECK(prototype->HasSameDexCacheResolvedMethods(method, image_pointer_size_));
auto* np = method->GetInterfaceMethodIfProxy(image_pointer_size_);
CHECK_EQ(prototype->GetDeclaringClass()->GetDexCache(), np->GetDexCache());
CHECK_EQ(prototype->GetDexMethodIndex(), method->GetDexMethodIndex());
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 5e9707c..f887b8e 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -245,11 +245,6 @@
EXPECT_TRUE(method->GetDeclaringClass() != nullptr);
EXPECT_TRUE(method->GetName() != nullptr);
EXPECT_TRUE(method->GetSignature() != Signature::NoSignature());
-
- EXPECT_TRUE(method->HasDexCacheResolvedMethods(kRuntimePointerSize));
- EXPECT_TRUE(method->HasSameDexCacheResolvedMethods(
- method->GetDeclaringClass()->GetDexCache()->GetResolvedMethods(),
- kRuntimePointerSize));
}
void AssertField(ObjPtr<mirror::Class> klass, ArtField* field)
diff --git a/runtime/compiler_filter_test.cc b/runtime/compiler_filter_test.cc
index 383f4e3..9b4f845 100644
--- a/runtime/compiler_filter_test.cc
+++ b/runtime/compiler_filter_test.cc
@@ -20,7 +20,7 @@
namespace art {
-static void TestCompilerFilterName(CompilerFilter::Filter filter, std::string name) {
+static void TestCompilerFilterName(CompilerFilter::Filter filter, const std::string& name) {
CompilerFilter::Filter parsed;
EXPECT_TRUE(CompilerFilter::ParseCompilerFilter(name.c_str(), &parsed));
EXPECT_EQ(filter, parsed);
@@ -28,7 +28,7 @@
EXPECT_EQ(name, CompilerFilter::NameOfFilter(filter));
}
-static void TestSafeModeFilter(CompilerFilter::Filter expected, std::string name) {
+static void TestSafeModeFilter(CompilerFilter::Filter expected, const std::string& name) {
CompilerFilter::Filter parsed;
EXPECT_TRUE(CompilerFilter::ParseCompilerFilter(name.c_str(), &parsed));
EXPECT_EQ(expected, CompilerFilter::GetSafeModeFilterFrom(parsed));
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index be3e4f8..8253739 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -83,7 +83,7 @@
ObjPtr<mirror::DexCache> dex_cache = caller->GetDexCache();
const DexFile* dex_file = dex_cache->GetDexFile();
const DexFile::MethodId& method_id = dex_file->GetMethodId(method_index);
- ArtMethod* inlined_method = caller->GetDexCacheResolvedMethod(method_index, kRuntimePointerSize);
+ ArtMethod* inlined_method = dex_cache->GetResolvedMethod(method_index, kRuntimePointerSize);
if (inlined_method != nullptr) {
DCHECK(!inlined_method->IsRuntimeMethod());
return inlined_method;
@@ -106,7 +106,7 @@
<< dex_file->GetMethodSignature(method_id) << " declared. "
<< "This must be due to duplicate classes or playing wrongly with class loaders";
}
- caller->SetDexCacheResolvedMethod(method_index, inlined_method, kRuntimePointerSize);
+ dex_cache->SetResolvedMethod(method_index, inlined_method, kRuntimePointerSize);
return inlined_method;
}
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index e08319d..c6abd28 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -1258,7 +1258,7 @@
// FindVirtualMethodFor... This is ok for FindDexMethodIndexInOtherDexFile that only cares
// about the name and signature.
uint32_t update_dex_cache_method_index = called->GetDexMethodIndex();
- if (!called->HasSameDexCacheResolvedMethods(caller, kRuntimePointerSize)) {
+ if (called->GetDexFile() != caller->GetDexFile()) {
// Calling from one dex file to another, need to compute the method index appropriate to
// the caller's dex file. Since we get here only if the original called was a runtime
// method, we've got the correct dex_file and a dex_method_idx from above.
@@ -1270,12 +1270,16 @@
called->FindDexMethodIndexInOtherDexFile(*caller_dex_file,
caller_method_name_and_sig_index);
}
- if ((update_dex_cache_method_index != DexFile::kDexNoIndex) &&
- (caller->GetDexCacheResolvedMethod(
- update_dex_cache_method_index, kRuntimePointerSize) != called)) {
- caller->SetDexCacheResolvedMethod(update_dex_cache_method_index,
- called,
- kRuntimePointerSize);
+ if (update_dex_cache_method_index != DexFile::kDexNoIndex) {
+ // Note: We do not need the read barrier for the dex cache as the SetResolvedMethod()
+ // operates on native (non-moveable) data and constants (num_resolved_methods_).
+ ObjPtr<mirror::DexCache> caller_dex_cache = caller->GetDexCache<kWithoutReadBarrier>();
+ if (caller_dex_cache->GetResolvedMethod(
+ update_dex_cache_method_index, kRuntimePointerSize) != called) {
+ caller_dex_cache->SetResolvedMethod(update_dex_cache_method_index,
+ called,
+ kRuntimePointerSize);
+ }
}
} else if (invoke_type == kStatic) {
const auto called_dex_method_idx = called->GetDexMethodIndex();
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 14e017a..1a48b46 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1128,7 +1128,7 @@
}
} else {
if (fixup_heap_objects_) {
- method->UpdateObjectsForImageRelocation(ForwardObjectAdapter(this), pointer_size_);
+ method->UpdateObjectsForImageRelocation(ForwardObjectAdapter(this));
}
method->UpdateEntrypoints<kWithoutReadBarrier>(ForwardCodeAdapter(this), pointer_size_);
}
diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h
index 314c45e..071d1ae 100644
--- a/runtime/generated/asm_support_gen.h
+++ b/runtime/generated/asm_support_gen.h
@@ -48,6 +48,10 @@
DEFINE_CHECK_EQ(static_cast<int32_t>(THREAD_CARD_TABLE_OFFSET), (static_cast<int32_t>(art::Thread:: CardTableOffset<art::kRuntimePointerSize>().Int32Value())))
#define CODEITEM_INSNS_OFFSET 16
DEFINE_CHECK_EQ(static_cast<int32_t>(CODEITEM_INSNS_OFFSET), (static_cast<int32_t>(__builtin_offsetof(art::DexFile::CodeItem, insns_))))
+#define MIRROR_CLASS_DEX_CACHE_OFFSET 16
+DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_CLASS_DEX_CACHE_OFFSET), (static_cast<int32_t>(art::mirror::Class:: DexCacheOffset().Int32Value())))
+#define MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET 48
+DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET), (static_cast<int32_t>(art::mirror::DexCache:: ResolvedMethodsOffset().Int32Value())))
#define MIRROR_OBJECT_CLASS_OFFSET 0
DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_OBJECT_CLASS_OFFSET), (static_cast<int32_t>(art::mirror::Object:: ClassOffset().Int32Value())))
#define MIRROR_OBJECT_LOCK_WORD_OFFSET 4
@@ -60,20 +64,18 @@
DEFINE_CHECK_EQ(static_cast<uint32_t>(ACCESS_FLAGS_CLASS_IS_INTERFACE), (static_cast<uint32_t>((art::kAccInterface))))
#define ACCESS_FLAGS_CLASS_IS_FINALIZABLE_BIT 0x1f
DEFINE_CHECK_EQ(static_cast<uint32_t>(ACCESS_FLAGS_CLASS_IS_FINALIZABLE_BIT), (static_cast<uint32_t>((art::MostSignificantBit(art::kAccClassIsFinalizable)))))
-#define ART_METHOD_DEX_CACHE_METHODS_OFFSET_32 20
-DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DEX_CACHE_METHODS_OFFSET_32), (static_cast<int32_t>(art::ArtMethod:: DexCacheResolvedMethodsOffset(art::PointerSize::k32).Int32Value())))
-#define ART_METHOD_DEX_CACHE_METHODS_OFFSET_64 24
-DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DEX_CACHE_METHODS_OFFSET_64), (static_cast<int32_t>(art::ArtMethod:: DexCacheResolvedMethodsOffset(art::PointerSize::k64).Int32Value())))
-#define ART_METHOD_JNI_OFFSET_32 24
+#define ART_METHOD_JNI_OFFSET_32 20
DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_JNI_OFFSET_32), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromJniOffset(art::PointerSize::k32).Int32Value())))
-#define ART_METHOD_JNI_OFFSET_64 32
+#define ART_METHOD_JNI_OFFSET_64 24
DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_JNI_OFFSET_64), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromJniOffset(art::PointerSize::k64).Int32Value())))
-#define ART_METHOD_QUICK_CODE_OFFSET_32 28
+#define ART_METHOD_QUICK_CODE_OFFSET_32 24
DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_QUICK_CODE_OFFSET_32), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromQuickCompiledCodeOffset(art::PointerSize::k32).Int32Value())))
-#define ART_METHOD_QUICK_CODE_OFFSET_64 40
+#define ART_METHOD_QUICK_CODE_OFFSET_64 32
DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_QUICK_CODE_OFFSET_64), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromQuickCompiledCodeOffset(art::PointerSize::k64).Int32Value())))
#define ART_METHOD_DECLARING_CLASS_OFFSET 0
DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DECLARING_CLASS_OFFSET), (static_cast<int32_t>(art::ArtMethod:: DeclaringClassOffset().Int32Value())))
+#define ART_METHOD_ACCESS_FLAGS_OFFSET 4
+DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_ACCESS_FLAGS_OFFSET), (static_cast<int32_t>(art::ArtMethod:: AccessFlagsOffset().Int32Value())))
#define STRING_DEX_CACHE_ELEMENT_SIZE_SHIFT 3
DEFINE_CHECK_EQ(static_cast<int32_t>(STRING_DEX_CACHE_ELEMENT_SIZE_SHIFT), (static_cast<int32_t>(art::WhichPowerOf2(sizeof(art::mirror::StringDexCachePair)))))
#define STRING_DEX_CACHE_SIZE_MINUS_ONE 1023
@@ -126,6 +128,10 @@
DEFINE_CHECK_EQ(static_cast<uint32_t>(OBJECT_ALIGNMENT_MASK_TOGGLED), (static_cast<uint32_t>(~static_cast<uint32_t>(art::kObjectAlignment - 1))))
#define OBJECT_ALIGNMENT_MASK_TOGGLED64 0xfffffffffffffff8
DEFINE_CHECK_EQ(static_cast<uint64_t>(OBJECT_ALIGNMENT_MASK_TOGGLED64), (static_cast<uint64_t>(~static_cast<uint64_t>(art::kObjectAlignment - 1))))
+#define ACC_OBSOLETE_METHOD 262144
+DEFINE_CHECK_EQ(static_cast<int32_t>(ACC_OBSOLETE_METHOD), (static_cast<int32_t>(art::kAccObsoleteMethod)))
+#define ACC_OBSOLETE_METHOD_SHIFT 18
+DEFINE_CHECK_EQ(static_cast<int32_t>(ACC_OBSOLETE_METHOD_SHIFT), (static_cast<int32_t>(art::WhichPowerOf2(art::kAccObsoleteMethod))))
#define ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE 128
DEFINE_CHECK_EQ(static_cast<int32_t>(ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE), (static_cast<int32_t>((art::gc::allocator::RosAlloc::kMaxThreadLocalBracketSize))))
#define ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT 3
diff --git a/runtime/image.cc b/runtime/image.cc
index 950ac5d..1f7e0f3 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -26,7 +26,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '4', '6', '\0' }; // Hash-based methods array.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '4', '7', '\0' }; // Smaller ArtMethod.
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 758c9e4..05384b4 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -105,6 +105,7 @@
have_field_read_listeners_(false),
have_field_write_listeners_(false),
have_exception_thrown_listeners_(false),
+ have_watched_frame_pop_listeners_(false),
have_branch_listeners_(false),
have_invoke_virtual_or_interface_listeners_(false),
deoptimized_methods_lock_("deoptimized methods lock", kDeoptimizedMethodsLock),
@@ -504,6 +505,11 @@
exception_thrown_listeners_,
listener,
&have_exception_thrown_listeners_);
+ PotentiallyAddListenerTo(kWatchedFramePop,
+ events,
+ watched_frame_pop_listeners_,
+ listener,
+ &have_watched_frame_pop_listeners_);
UpdateInterpreterHandlerTable();
}
@@ -581,6 +587,11 @@
exception_thrown_listeners_,
listener,
&have_exception_thrown_listeners_);
+ PotentiallyRemoveListenerFrom(kWatchedFramePop,
+ events,
+ watched_frame_pop_listeners_,
+ listener,
+ &have_watched_frame_pop_listeners_);
UpdateInterpreterHandlerTable();
}
@@ -1042,6 +1053,14 @@
}
}
+void Instrumentation::WatchedFramePopImpl(Thread* thread, const ShadowFrame& frame) const {
+ for (InstrumentationListener* listener : watched_frame_pop_listeners_) {
+ if (listener != nullptr) {
+ listener->WatchedFramePop(thread, frame);
+ }
+ }
+}
+
void Instrumentation::FieldReadEventImpl(Thread* thread,
ObjPtr<mirror::Object> this_object,
ArtMethod* method,
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index f6efb5f..114db76 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -38,6 +38,7 @@
class ArtMethod;
template <typename T> class Handle;
union JValue;
+class ShadowFrame;
class Thread;
namespace instrumentation {
@@ -143,6 +144,15 @@
uint32_t dex_pc,
ArtMethod* callee)
REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+
+ // Call-back when a shadow_frame with the needs_notify_pop_ boolean set is popped off the stack by
+ // either return or exceptions. Normally instrumentation listeners should ensure that there are
+ // shadow-frames by deoptimizing stacks.
+ virtual void WatchedFramePop(Thread* thread ATTRIBUTE_UNUSED,
+ const ShadowFrame& frame ATTRIBUTE_UNUSED)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return;
+ }
};
// Instrumentation is a catch-all for when extra information is required from the runtime. The
@@ -161,6 +171,7 @@
kExceptionThrown = 0x40,
kBranch = 0x80,
kInvokeVirtualOrInterface = 0x100,
+ kWatchedFramePop = 0x200,
};
enum class InstrumentationLevel {
@@ -334,11 +345,16 @@
return have_invoke_virtual_or_interface_listeners_;
}
+ bool HasWatchedFramePopListeners() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ return have_watched_frame_pop_listeners_;
+ }
+
bool IsActive() const REQUIRES_SHARED(Locks::mutator_lock_) {
return have_dex_pc_listeners_ || have_method_entry_listeners_ || have_method_exit_listeners_ ||
have_field_read_listeners_ || have_field_write_listeners_ ||
have_exception_thrown_listeners_ || have_method_unwind_listeners_ ||
- have_branch_listeners_ || have_invoke_virtual_or_interface_listeners_;
+ have_branch_listeners_ || have_invoke_virtual_or_interface_listeners_ ||
+ have_watched_frame_pop_listeners_;
}
// Any instrumentation *other* than what is needed for Jit profiling active?
@@ -346,7 +362,7 @@
return have_dex_pc_listeners_ || have_method_exit_listeners_ ||
have_field_read_listeners_ || have_field_write_listeners_ ||
have_exception_thrown_listeners_ || have_method_unwind_listeners_ ||
- have_branch_listeners_;
+ have_branch_listeners_ || have_watched_frame_pop_listeners_;
}
// Inform listeners that a method has been entered. A dex PC is provided as we may install
@@ -424,6 +440,14 @@
}
}
+ // Inform listeners that a branch has been taken (only supported by the interpreter).
+ void WatchedFramePopped(Thread* thread, const ShadowFrame& frame) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (UNLIKELY(HasWatchedFramePopListeners())) {
+ WatchedFramePopImpl(thread, frame);
+ }
+ }
+
// Inform listeners that an exception was thrown.
void ExceptionThrownEvent(Thread* thread, mirror::Throwable* exception_object) const
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -531,6 +555,8 @@
uint32_t dex_pc,
ArtMethod* callee) const
REQUIRES_SHARED(Locks::mutator_lock_);
+ void WatchedFramePopImpl(Thread* thread, const ShadowFrame& frame) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
void FieldReadEventImpl(Thread* thread,
ObjPtr<mirror::Object> this_object,
ArtMethod* method,
@@ -602,6 +628,9 @@
// Do we have any exception thrown listeners? Short-cut to avoid taking the instrumentation_lock_.
bool have_exception_thrown_listeners_ GUARDED_BY(Locks::mutator_lock_);
+ // Do we have any frame pop listeners? Short-cut to avoid taking the instrumentation_lock_.
+ bool have_watched_frame_pop_listeners_ GUARDED_BY(Locks::mutator_lock_);
+
// Do we have any branch listeners? Short-cut to avoid taking the instrumentation_lock_.
bool have_branch_listeners_ GUARDED_BY(Locks::mutator_lock_);
@@ -633,6 +662,7 @@
std::list<InstrumentationListener*> field_read_listeners_ GUARDED_BY(Locks::mutator_lock_);
std::list<InstrumentationListener*> field_write_listeners_ GUARDED_BY(Locks::mutator_lock_);
std::list<InstrumentationListener*> exception_thrown_listeners_ GUARDED_BY(Locks::mutator_lock_);
+ std::list<InstrumentationListener*> watched_frame_pop_listeners_ GUARDED_BY(Locks::mutator_lock_);
// The set of methods being deoptimized (by the debugger) which must be executed with interpreter
// only.
diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc
index 19ce299..7390f4f 100644
--- a/runtime/instrumentation_test.cc
+++ b/runtime/instrumentation_test.cc
@@ -28,6 +28,7 @@
#include "jvalue.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
+#include "interpreter/shadow_frame.h"
#include "thread-inl.h"
#include "thread_list.h"
#include "well_known_classes.h"
@@ -48,7 +49,8 @@
received_field_written_object_event(false),
received_exception_thrown_event(false),
received_branch_event(false),
- received_invoke_virtual_or_interface_event(false) {}
+ received_invoke_virtual_or_interface_event(false),
+ received_watched_frame_pop(false) {}
virtual ~TestInstrumentationListener() {}
@@ -146,6 +148,11 @@
received_invoke_virtual_or_interface_event = true;
}
+ void WatchedFramePop(Thread* thread ATTRIBUTE_UNUSED, const ShadowFrame& frame ATTRIBUTE_UNUSED)
+ OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ received_watched_frame_pop = true;
+ }
+
void Reset() {
received_method_enter_event = false;
received_method_exit_event = false;
@@ -158,6 +165,7 @@
received_exception_thrown_event = false;
received_branch_event = false;
received_invoke_virtual_or_interface_event = false;
+ received_watched_frame_pop = false;
}
bool received_method_enter_event;
@@ -171,6 +179,7 @@
bool received_exception_thrown_event;
bool received_branch_event;
bool received_invoke_virtual_or_interface_event;
+ bool received_watched_frame_pop;
private:
DISALLOW_COPY_AND_ASSIGN(TestInstrumentationListener);
@@ -221,6 +230,7 @@
mirror::Object* const event_obj = nullptr;
const uint32_t event_dex_pc = 0;
+ ShadowFrameAllocaUniquePtr test_frame = CREATE_SHADOW_FRAME(0, nullptr, event_method, 0);
// Check the listener is registered and is notified of the event.
EXPECT_TRUE(HasEventListener(instr, instrumentation_event));
@@ -231,7 +241,8 @@
event_method,
event_obj,
event_field,
- event_dex_pc);
+ event_dex_pc,
+ *test_frame);
EXPECT_TRUE(DidListenerReceiveEvent(listener, instrumentation_event, with_object));
listener.Reset();
@@ -250,7 +261,8 @@
event_method,
event_obj,
event_field,
- event_dex_pc);
+ event_dex_pc,
+ *test_frame);
EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event, with_object));
}
@@ -361,6 +373,8 @@
return instr->HasBranchListeners();
case instrumentation::Instrumentation::kInvokeVirtualOrInterface:
return instr->HasInvokeVirtualOrInterfaceListeners();
+ case instrumentation::Instrumentation::kWatchedFramePop:
+ return instr->HasWatchedFramePopListeners();
default:
LOG(FATAL) << "Unknown instrumentation event " << event_type;
UNREACHABLE();
@@ -373,7 +387,8 @@
ArtMethod* method,
mirror::Object* obj,
ArtField* field,
- uint32_t dex_pc)
+ uint32_t dex_pc,
+ const ShadowFrame& frame)
REQUIRES_SHARED(Locks::mutator_lock_) {
switch (event_type) {
case instrumentation::Instrumentation::kMethodEntered:
@@ -411,6 +426,9 @@
case instrumentation::Instrumentation::kInvokeVirtualOrInterface:
instr->InvokeVirtualOrInterface(self, obj, method, dex_pc, method);
break;
+ case instrumentation::Instrumentation::kWatchedFramePop:
+ instr->WatchedFramePopped(self, frame);
+ break;
default:
LOG(FATAL) << "Unknown instrumentation event " << event_type;
UNREACHABLE();
@@ -441,6 +459,8 @@
return listener.received_branch_event;
case instrumentation::Instrumentation::kInvokeVirtualOrInterface:
return listener.received_invoke_virtual_or_interface_event;
+ case instrumentation::Instrumentation::kWatchedFramePop:
+ return listener.received_watched_frame_pop;
default:
LOG(FATAL) << "Unknown instrumentation event " << event_type;
UNREACHABLE();
@@ -527,6 +547,10 @@
TestEvent(instrumentation::Instrumentation::kFieldRead);
}
+TEST_F(InstrumentationTest, WatchedFramePop) {
+ TestEvent(instrumentation::Instrumentation::kWatchedFramePop);
+}
+
TEST_F(InstrumentationTest, FieldWriteObjectEvent) {
ScopedObjectAccess soa(Thread::Current());
jobject class_loader = LoadDex("Instrumentation");
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 8c825f3..ae461fd 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -431,6 +431,9 @@
uint32_t found_dex_pc = shadow_frame.GetMethod()->FindCatchBlock(
hs.NewHandle(exception->GetClass()), dex_pc, &clear_exception);
if (found_dex_pc == DexFile::kDexNoIndex && instrumentation != nullptr) {
+ if (shadow_frame.NeedsNotifyPop()) {
+ instrumentation->WatchedFramePopped(self, shadow_frame);
+ }
// Exception is not caught by the current method. We will unwind to the
// caller. Notify any instrumentation listener.
instrumentation->MethodUnwindEvent(self, shadow_frame.GetThisObject(),
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 0c5a45f..f352960 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -150,6 +150,37 @@
}
}
+static bool NeedsMethodExitEvent(const instrumentation::Instrumentation* ins)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return ins->HasMethodExitListeners() || ins->HasWatchedFramePopListeners();
+}
+
+// Sends the normal method exit event. Returns true if the events succeeded and false if there is a
+// pending exception.
+NO_INLINE static bool SendMethodExitEvents(Thread* self,
+ const instrumentation::Instrumentation* instrumentation,
+ const ShadowFrame& frame,
+ ObjPtr<mirror::Object> thiz,
+ ArtMethod* method,
+ uint32_t dex_pc,
+ const JValue& result)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool had_event = false;
+ if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ had_event = true;
+ instrumentation->MethodExitEvent(self, thiz.Ptr(), method, dex_pc, result);
+ }
+ if (UNLIKELY(frame.NeedsNotifyPop() && instrumentation->HasWatchedFramePopListeners())) {
+ had_event = true;
+ instrumentation->WatchedFramePopped(self, frame);
+ }
+ if (UNLIKELY(had_event)) {
+ return !self->IsExceptionPending();
+ } else {
+ return true;
+ }
+}
+
template<bool do_access_check, bool transaction_active>
JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register,
@@ -262,14 +293,15 @@
JValue result;
self->AllowThreadSuspension();
HANDLE_MONITOR_CHECKS();
- if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
- instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
- shadow_frame.GetMethod(), inst->GetDexPc(insns),
- result);
- if (UNLIKELY(self->IsExceptionPending())) {
- // Don't send another method exit event.
- HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
- }
+ if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
+ !SendMethodExitEvents(self,
+ instrumentation,
+ shadow_frame,
+ shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(),
+ inst->GetDexPc(insns),
+ result))) {
+ HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
}
if (interpret_one_instruction) {
/* Signal mterp to return to caller */
@@ -283,14 +315,15 @@
JValue result;
self->AllowThreadSuspension();
HANDLE_MONITOR_CHECKS();
- if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
- instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
- shadow_frame.GetMethod(), inst->GetDexPc(insns),
- result);
- if (UNLIKELY(self->IsExceptionPending())) {
- // Don't send another method exit event.
- HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
- }
+ if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
+ !SendMethodExitEvents(self,
+ instrumentation,
+ shadow_frame,
+ shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(),
+ inst->GetDexPc(insns),
+ result))) {
+ HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
}
if (interpret_one_instruction) {
/* Signal mterp to return to caller */
@@ -305,14 +338,15 @@
result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data)));
self->AllowThreadSuspension();
HANDLE_MONITOR_CHECKS();
- if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
- instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
- shadow_frame.GetMethod(), inst->GetDexPc(insns),
- result);
- if (UNLIKELY(self->IsExceptionPending())) {
- // Don't send another method exit event.
- HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
- }
+ if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
+ !SendMethodExitEvents(self,
+ instrumentation,
+ shadow_frame,
+ shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(),
+ inst->GetDexPc(insns),
+ result))) {
+ HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
}
if (interpret_one_instruction) {
/* Signal mterp to return to caller */
@@ -326,14 +360,15 @@
result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data)));
self->AllowThreadSuspension();
HANDLE_MONITOR_CHECKS();
- if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
- instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
- shadow_frame.GetMethod(), inst->GetDexPc(insns),
- result);
- if (UNLIKELY(self->IsExceptionPending())) {
- // Don't send another method exit event.
- HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
- }
+ if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
+ !SendMethodExitEvents(self,
+ instrumentation,
+ shadow_frame,
+ shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(),
+ inst->GetDexPc(insns),
+ result))) {
+ HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
}
if (interpret_one_instruction) {
/* Signal mterp to return to caller */
@@ -367,17 +402,18 @@
}
}
result.SetL(obj_result);
- if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
- instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
- shadow_frame.GetMethod(), inst->GetDexPc(insns),
- result);
- if (UNLIKELY(self->IsExceptionPending())) {
- // Don't send another method exit event.
- HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
- }
- // Re-load since it might have moved during the MethodExitEvent.
- result.SetL(shadow_frame.GetVRegReference(ref_idx));
+ if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
+ !SendMethodExitEvents(self,
+ instrumentation,
+ shadow_frame,
+ shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(),
+ inst->GetDexPc(insns),
+ result))) {
+ HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
}
+ // Re-load since it might have moved during the MethodExitEvent.
+ result.SetL(shadow_frame.GetVRegReference(ref_idx));
if (interpret_one_instruction) {
/* Signal mterp to return to caller */
shadow_frame.SetDexPC(DexFile::kDexNoIndex);
diff --git a/runtime/interpreter/shadow_frame.h b/runtime/interpreter/shadow_frame.h
index 05768cd..6903af2 100644
--- a/runtime/interpreter/shadow_frame.h
+++ b/runtime/interpreter/shadow_frame.h
@@ -361,6 +361,14 @@
return result_register_;
}
+ bool NeedsNotifyPop() const {
+ return needs_notify_pop_;
+ }
+
+ void SetNotifyPop(bool notify) {
+ needs_notify_pop_ = notify;
+ }
+
private:
ShadowFrame(uint32_t num_vregs, ShadowFrame* link, ArtMethod* method,
uint32_t dex_pc, bool has_reference_array)
@@ -372,7 +380,8 @@
number_of_vregs_(num_vregs),
dex_pc_(dex_pc),
cached_hotness_countdown_(0),
- hotness_countdown_(0) {
+ hotness_countdown_(0),
+ needs_notify_pop_(0) {
// TODO(iam): Remove this parameter, it's an an artifact of portable removal
DCHECK(has_reference_array);
if (has_reference_array) {
@@ -404,6 +413,9 @@
uint32_t dex_pc_;
int16_t cached_hotness_countdown_;
int16_t hotness_countdown_;
+ // TODO Might be worth it to try to bit-pack this into some other field to reduce stack usage.
+ // NB alignment requires that this field takes 4 bytes. Only 1 bit is actually ever used.
+ bool needs_notify_pop_;
// This is a two-part array:
// - [0..number_of_vregs) holds the raw virtual registers, and each element here is always 4
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index c79b5c9..d9cfa53 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -75,7 +75,7 @@
static constexpr bool kPrintDlOpenErrorMessage = false;
// If true, we advise the kernel about dex file mem map accesses.
-static constexpr bool kMadviseDexFileAccesses = false;
+static constexpr bool kMadviseDexFileAccesses = true;
// Note for OatFileBase and descendents:
//
@@ -1498,14 +1498,20 @@
// Madvise the dex file based on the state we are moving to.
void OatDexFile::MadviseDexFile(const DexFile& dex_file, MadviseState state) {
- if (!kMadviseDexFileAccesses) {
+ const bool low_ram = Runtime::Current()->GetHeap()->IsLowMemoryMode();
+ // TODO: Also do madvise hints for non low ram devices.
+ if (!kMadviseDexFileAccesses || !low_ram) {
return;
}
if (state == MadviseState::kMadviseStateAtLoad) {
- // Default every dex file to MADV_RANDOM when its loaded by default.
- MadviseLargestPageAlignedRegion(dex_file.Begin(),
- dex_file.Begin() + dex_file.Size(),
- MADV_RANDOM);
+ if (low_ram) {
+ // Default every dex file to MADV_RANDOM when its loaded by default for low ram devices.
+ // Other devices have enough page cache to get performance benefits from loading more pages
+ // into the page cache.
+ MadviseLargestPageAlignedRegion(dex_file.Begin(),
+ dex_file.Begin() + dex_file.Size(),
+ MADV_RANDOM);
+ }
}
const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
if (oat_dex_file != nullptr) {
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
index a4d6c3d..ac2ed9e 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -258,7 +258,7 @@
/*out*/DexFile const** final_dex_file ATTRIBUTE_UNUSED,
/*out*/DexFile::ClassDef const** final_class_def ATTRIBUTE_UNUSED)
OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
- std::string location(initial_dex_file.GetLocation());
+ const std::string& location = initial_dex_file.GetLocation();
std::string event =
std::string("PreDefine:") + descriptor + " <" +
location.substr(location.rfind('/') + 1, location.size()) + ">";
diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc
index 82b9af3..6ff9666 100644
--- a/runtime/ti/agent.cc
+++ b/runtime/ti/agent.cc
@@ -123,7 +123,7 @@
}
}
-Agent::Agent(std::string arg)
+Agent::Agent(const std::string& arg)
: dlopen_handle_(nullptr),
onload_(nullptr),
onattach_(nullptr),
diff --git a/runtime/ti/agent.h b/runtime/ti/agent.h
index f98e387..d6f1f2e 100644
--- a/runtime/ti/agent.h
+++ b/runtime/ti/agent.h
@@ -77,7 +77,7 @@
return DoLoadHelper(true, call_res, error_msg);
}
- explicit Agent(std::string arg);
+ explicit Agent(const std::string& arg);
Agent(const Agent& other);
Agent& operator=(const Agent& other);
diff --git a/test/004-JniTest/build b/test/004-JniTest/build
index c8440fc..e563d73 100755
--- a/test/004-JniTest/build
+++ b/test/004-JniTest/build
@@ -38,4 +38,30 @@
export -f javac_wrapper
export JAVAC=javac_wrapper
+######################################################################
+
+# Use the original dx with no extra magic or pessimizing flags.
+# This ensures that any default optimizations that dx do would not break JNI.
+
+export ORIGINAL_DX="$DX"
+
+# Filter out --debug flag from dx.
+function dx_wrapper {
+ local args=("$@")
+ local args_filtered=()
+ for i in "${args[@]}"; do
+ case "$i" in
+ --debug)
+ ;;
+ *)
+ args_filtered+=("$i")
+ ;;
+ esac
+ done
+ "$ORIGINAL_DX" "${args_filtered[@]}"
+}
+
+export -f dx_wrapper
+export DX=dx_wrapper
+
./default-build "$@"
diff --git a/test/004-JniTest/expected.txt b/test/004-JniTest/expected.txt
index f7e404d..7e85ab1 100644
--- a/test/004-JniTest/expected.txt
+++ b/test/004-JniTest/expected.txt
@@ -58,3 +58,5 @@
hi-lambda: λ
hi-default δλ
hi-default δλ
+Clinit Lookup: ClassWithoutClinit: <NSME Exception>
+Clinit Lookup: ClassWithClinit: Main$ClassWithClinit()(Class: class java.lang.reflect.Constructor)
diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index a34d633..bc5a0a6 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -776,6 +776,18 @@
return a + b + c;
}
+extern "C" JNIEXPORT jobject JNICALL Java_Main_lookupClinit(JNIEnv* env, jclass, jclass kls) {
+ jmethodID clinit_id = env->GetStaticMethodID(kls, "<clinit>", "()V");
+
+ if (clinit_id != nullptr) {
+ jobject obj = env->ToReflectedMethod(kls, clinit_id, /*isStatic*/ true);
+ CHECK(obj != nullptr);
+ return obj;
+ } else {
+ return nullptr;
+ }
+}
+
extern "C" JNIEXPORT jboolean JNICALL Java_Main_isSlowDebug(JNIEnv*, jclass) {
// Return whether slow-debug is on. Only relevant for debug builds.
if (kIsDebugBuild) {
diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java
index 0c4ed89..fe5f4e3 100644
--- a/test/004-JniTest/src/Main.java
+++ b/test/004-JniTest/src/Main.java
@@ -56,6 +56,8 @@
registerNativesJniTest();
testFastNativeMethods();
testCriticalNativeMethods();
+
+ testClinitMethodLookup();
}
private static native boolean registerNativesJniTest();
@@ -314,6 +316,36 @@
}
private static native boolean isSlowDebug();
+
+ private static void testClinitMethodLookup() {
+ // Expect this to print <NSME Exception>
+ try {
+ System.out.println("Clinit Lookup: ClassWithoutClinit: " + methodString(lookupClinit(ClassWithoutClinit.class)));
+ } catch (NoSuchMethodError e) {
+ System.out.println("Clinit Lookup: ClassWithoutClinit: <NSME Exception>");
+ }
+ // Expect this to print <clinit>
+ try {
+ System.out.println("Clinit Lookup: ClassWithClinit: " + methodString(lookupClinit(ClassWithClinit.class)));
+ } catch (NoSuchMethodError e) {
+ System.out.println("Clinit Lookup: ClassWithClinit: <NSME Exception>");
+ }
+ }
+
+ private static String methodString(java.lang.reflect.Executable method) {
+ if (method == null) {
+ return "<<null>>";
+ } else {
+ return method.toString() + "(Class: " + method.getClass().toString() + ")";
+ }
+ }
+ private static native java.lang.reflect.Executable lookupClinit(Class kls);
+
+ private static class ClassWithoutClinit {
+ }
+ private static class ClassWithClinit {
+ static {}
+ }
}
@FunctionalInterface
diff --git a/test/021-string2/src/Main.java b/test/021-string2/src/Main.java
index 3b81d8e..c713aa4 100644
--- a/test/021-string2/src/Main.java
+++ b/test/021-string2/src/Main.java
@@ -556,12 +556,24 @@
Assert.assertTrue($noinline$equalsConstString0(""));
Assert.assertFalse($noinline$equalsConstString0("1"));
+ Assert.assertTrue($noinline$equalsConstString3("012"));
+ Assert.assertFalse($noinline$equalsConstString3("01"));
+ Assert.assertFalse($noinline$equalsConstString3("0123"));
+ Assert.assertFalse($noinline$equalsConstString3("01x"));
+ Assert.assertFalse($noinline$equalsConstString3("01\u0440"));
+
Assert.assertTrue($noinline$equalsConstString7("0123456"));
Assert.assertFalse($noinline$equalsConstString7("012345"));
Assert.assertFalse($noinline$equalsConstString7("01234567"));
Assert.assertFalse($noinline$equalsConstString7("012345x"));
Assert.assertFalse($noinline$equalsConstString7("012345\u0440"));
+ Assert.assertTrue($noinline$equalsConstString12("012345678901"));
+ Assert.assertFalse($noinline$equalsConstString12("01234567890"));
+ Assert.assertFalse($noinline$equalsConstString12("0123456789012"));
+ Assert.assertFalse($noinline$equalsConstString12("01234567890x"));
+ Assert.assertFalse($noinline$equalsConstString12("01234567890\u0440"));
+
Assert.assertTrue($noinline$equalsConstString14("01234567890123"));
Assert.assertFalse($noinline$equalsConstString14("0123456789012"));
Assert.assertFalse($noinline$equalsConstString14("012345678901234"));
@@ -587,12 +599,24 @@
Assert.assertFalse(
$noinline$equalsConstString35("0123456789012345678901234567890123\u0440"));
+ Assert.assertTrue($noinline$equalsConstNonAsciiString3("\u044012"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString3("\u04401"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString3("\u0440123"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString3("\u04401x"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString3("012"));
+
Assert.assertTrue($noinline$equalsConstNonAsciiString7("\u0440123456"));
Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u044012345"));
Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u04401234567"));
Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u044012345x"));
Assert.assertFalse($noinline$equalsConstNonAsciiString7("0123456"));
+ Assert.assertTrue($noinline$equalsConstNonAsciiString12("\u044012345678901"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString12("\u04401234567890"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString12("\u0440123456789012"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString12("\u04401234567890x"));
+ Assert.assertFalse($noinline$equalsConstNonAsciiString12("012345678901"));
+
Assert.assertTrue($noinline$equalsConstNonAsciiString14("\u04401234567890123"));
Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u0440123456789012"));
Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u044012345678901234"));
@@ -631,12 +655,24 @@
Assert.assertTrue($noinline$constString0Equals(""));
Assert.assertFalse($noinline$constString0Equals("1"));
+ Assert.assertTrue($noinline$constString3Equals("012"));
+ Assert.assertFalse($noinline$constString3Equals("01"));
+ Assert.assertFalse($noinline$constString3Equals("0123"));
+ Assert.assertFalse($noinline$constString3Equals("01x"));
+ Assert.assertFalse($noinline$constString3Equals("01\u0440"));
+
Assert.assertTrue($noinline$constString7Equals("0123456"));
Assert.assertFalse($noinline$constString7Equals("012345"));
Assert.assertFalse($noinline$constString7Equals("01234567"));
Assert.assertFalse($noinline$constString7Equals("012345x"));
Assert.assertFalse($noinline$constString7Equals("012345\u0440"));
+ Assert.assertTrue($noinline$constString12Equals("012345678901"));
+ Assert.assertFalse($noinline$constString12Equals("01234567890"));
+ Assert.assertFalse($noinline$constString12Equals("0123456789012"));
+ Assert.assertFalse($noinline$constString12Equals("01234567890x"));
+ Assert.assertFalse($noinline$constString12Equals("01234567890\u0440"));
+
Assert.assertTrue($noinline$constString14Equals("01234567890123"));
Assert.assertFalse($noinline$constString14Equals("0123456789012"));
Assert.assertFalse($noinline$constString14Equals("012345678901234"));
@@ -662,12 +698,24 @@
Assert.assertFalse(
$noinline$constString35Equals("0123456789012345678901234567890123\u0040"));
+ Assert.assertTrue($noinline$constNonAsciiString3Equals("\u044012"));
+ Assert.assertFalse($noinline$constNonAsciiString3Equals("\u04401"));
+ Assert.assertFalse($noinline$constNonAsciiString3Equals("\u0440123"));
+ Assert.assertFalse($noinline$constNonAsciiString3Equals("\u04401x"));
+ Assert.assertFalse($noinline$constNonAsciiString3Equals("0123456"));
+
Assert.assertTrue($noinline$constNonAsciiString7Equals("\u0440123456"));
Assert.assertFalse($noinline$constNonAsciiString7Equals("\u044012345"));
Assert.assertFalse($noinline$constNonAsciiString7Equals("\u04401234567"));
Assert.assertFalse($noinline$constNonAsciiString7Equals("\u044012345x"));
Assert.assertFalse($noinline$constNonAsciiString7Equals("0123456"));
+ Assert.assertTrue($noinline$constNonAsciiString12Equals("\u044012345678901"));
+ Assert.assertFalse($noinline$constNonAsciiString12Equals("\u04401234567890"));
+ Assert.assertFalse($noinline$constNonAsciiString12Equals("\u0440123456789012"));
+ Assert.assertFalse($noinline$constNonAsciiString12Equals("\u04401234567890x"));
+ Assert.assertFalse($noinline$constNonAsciiString12Equals("012345678901"));
+
Assert.assertTrue($noinline$constNonAsciiString14Equals("\u04401234567890123"));
Assert.assertFalse($noinline$constNonAsciiString14Equals("\u0440123456789012"));
Assert.assertFalse($noinline$constNonAsciiString14Equals("\u044012345678901234"));
@@ -708,134 +756,138 @@
}
public static boolean $noinline$equalsConstString0(String s) {
- if (doThrow) { throw new Error(); }
return s.equals("");
}
+ public static boolean $noinline$equalsConstString3(String s) {
+ return s.equals("012");
+ }
+
public static boolean $noinline$equalsConstString7(String s) {
- if (doThrow) { throw new Error(); }
return s.equals("0123456");
}
+ public static boolean $noinline$equalsConstString12(String s) {
+ return s.equals("012345678901");
+ }
+
public static boolean $noinline$equalsConstString14(String s) {
- if (doThrow) { throw new Error(); }
return s.equals("01234567890123");
}
public static boolean $noinline$equalsConstString24(String s) {
- if (doThrow) { throw new Error(); }
return s.equals("012345678901234567890123");
}
public static boolean $noinline$equalsConstString29(String s) {
- if (doThrow) { throw new Error(); }
return s.equals("01234567890123456789012345678");
}
public static boolean $noinline$equalsConstString35(String s) {
- if (doThrow) { throw new Error(); }
return s.equals("01234567890123456789012345678901234");
}
+ public static boolean $noinline$equalsConstNonAsciiString3(String s) {
+ return s.equals("\u044012");
+ }
+
public static boolean $noinline$equalsConstNonAsciiString7(String s) {
- if (doThrow) { throw new Error(); }
return s.equals("\u0440123456");
}
+ public static boolean $noinline$equalsConstNonAsciiString12(String s) {
+ return s.equals("\u044012345678901");
+ }
+
public static boolean $noinline$equalsConstNonAsciiString14(String s) {
- if (doThrow) { throw new Error(); }
return s.equals("\u04401234567890123");
}
public static boolean $noinline$equalsConstNonAsciiString24(String s) {
- if (doThrow) { throw new Error(); }
return s.equals("\u044012345678901234567890123");
}
public static boolean $noinline$equalsConstNonAsciiString29(String s) {
- if (doThrow) { throw new Error(); }
return s.equals("\u04401234567890123456789012345678");
}
public static boolean $noinline$equalsConstNonAsciiString35(String s) {
- if (doThrow) { throw new Error(); }
return s.equals("\u04401234567890123456789012345678901234");
}
public static boolean $noinline$constString0Equals(String s) {
- if (doThrow) { throw new Error(); }
return s.equals("");
}
+ public static boolean $noinline$constString3Equals(String s) {
+ return "012".equals(s);
+ }
+
public static boolean $noinline$constString7Equals(String s) {
- if (doThrow) { throw new Error(); }
return "0123456".equals(s);
}
+ public static boolean $noinline$constString12Equals(String s) {
+ return "012345678901".equals(s);
+ }
+
public static boolean $noinline$constString14Equals(String s) {
- if (doThrow) { throw new Error(); }
return "01234567890123".equals(s);
}
public static boolean $noinline$constString24Equals(String s) {
- if (doThrow) { throw new Error(); }
return "012345678901234567890123".equals(s);
}
public static boolean $noinline$constString29Equals(String s) {
- if (doThrow) { throw new Error(); }
return "01234567890123456789012345678".equals(s);
}
public static boolean $noinline$constString35Equals(String s) {
- if (doThrow) { throw new Error(); }
return "01234567890123456789012345678901234".equals(s);
}
+ public static boolean $noinline$constNonAsciiString3Equals(String s) {
+ return "\u044012".equals(s);
+ }
+
public static boolean $noinline$constNonAsciiString7Equals(String s) {
- if (doThrow) { throw new Error(); }
return "\u0440123456".equals(s);
}
+ public static boolean $noinline$constNonAsciiString12Equals(String s) {
+ return "\u044012345678901".equals(s);
+ }
+
public static boolean $noinline$constNonAsciiString14Equals(String s) {
- if (doThrow) { throw new Error(); }
return "\u04401234567890123".equals(s);
}
public static boolean $noinline$constNonAsciiString24Equals(String s) {
- if (doThrow) { throw new Error(); }
return "\u044012345678901234567890123".equals(s);
}
public static boolean $noinline$constNonAsciiString29Equals(String s) {
- if (doThrow) { throw new Error(); }
return "\u04401234567890123456789012345678".equals(s);
}
public static boolean $noinline$constNonAsciiString35Equals(String s) {
- if (doThrow) { throw new Error(); }
return "\u04401234567890123456789012345678901234".equals(s);
}
public static int $noinline$compareTo(String lhs, String rhs) {
- if (doThrow) { throw new Error(); }
return lhs.compareTo(rhs);
}
public static boolean $noinline$equals(String lhs, String rhs) {
- if (doThrow) { throw new Error(); }
return lhs.equals(rhs);
}
public static int $noinline$indexOf(String lhs, int ch) {
- if (doThrow) { throw new Error(); }
return lhs.indexOf(ch);
}
public static int $noinline$indexOf(String lhs, int ch, int fromIndex) {
- if (doThrow) { throw new Error(); }
return lhs.indexOf(ch, fromIndex);
}
-
- public static boolean doThrow = false;
}
diff --git a/test/1922-owned-monitors-info/expected.txt b/test/1922-owned-monitors-info/expected.txt
new file mode 100644
index 0000000..f66d1d5
--- /dev/null
+++ b/test/1922-owned-monitors-info/expected.txt
@@ -0,0 +1,678 @@
+owner-monitors, This thread
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockOther]
+Owned monitors: [NamedLock("Lock 1")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [NamedLock("Lock 2"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 2"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 2"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 3")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 3"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 3"), Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 3"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [class art.Test1922$Target]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockSync, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Current thread test: owned-monitor")]
+owner-monitors, no suspend, Other thread
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockOther]
+Owned monitors: [NamedLock("Lock 1")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 3")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 3"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 3"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [class art.Test1922$Target]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockSync, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: false): owned-monitor")]
+owner-monitors, suspend, Other thread
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockOther]
+Owned monitors: [NamedLock("Lock 1")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 3")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 3"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 3"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [class art.Test1922$Target]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockSync, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: true): owned-monitor")]
+owner-monitors-stack-depth, This thread
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockOther]
+Owned monitors: [{ depth: 2, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra]
+Owned monitors: [{ depth: 2, monitor: "NamedLock("Lock 2")" }, { depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 4, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 4, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 4, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 4, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 4, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [{ depth: 2, monitor: "NamedLock("Lock 2")" }, { depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 4, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [{ depth: 2, monitor: "NamedLock("Lock 2")" }, { depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 4, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 4, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 4, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 4, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 4, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockSync, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }]
+owner-monitors-stack-depth, no suspend, other thread
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockOther]
+Owned monitors: [{ depth: 1, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra]
+Owned monitors: [{ depth: 1, monitor: "NamedLock("Lock 2")" }, { depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 3, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 3, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 3, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 1, monitor: "class art.Test1922$Target" }, { depth: 3, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 3, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [{ depth: 1, monitor: "NamedLock("Lock 2")" }, { depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 3, monitor: "NamedLock("Lock 2")" }, { depth: 3, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: 3, monitor: "NamedLock("Lock 2")" }, { depth: 3, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 1, monitor: "class art.Test1922$Target" }, { depth: 3, monitor: "NamedLock("Lock 2")" }, { depth: 3, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 3, monitor: "NamedLock("Lock 2")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 1, monitor: "class art.Test1922$Target" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 1, monitor: "class art.Test1922$Target" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 1, monitor: "class art.Test1922$Target" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 3, monitor: "class art.Test1922$Target" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [{ depth: 1, monitor: "NamedLock("Lock 2")" }, { depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 3, monitor: "NamedLock("Lock 2")" }, { depth: 3, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: 3, monitor: "NamedLock("Lock 2")" }, { depth: 3, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 1, monitor: "class art.Test1922$Target" }, { depth: 3, monitor: "NamedLock("Lock 2")" }, { depth: 3, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 3, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 5, monitor: "NamedLock("Lock 2")" }, { depth: 5, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 5, monitor: "NamedLock("Lock 2")" }, { depth: 5, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 1, monitor: "class art.Test1922$Target" }, { depth: 5, monitor: "NamedLock("Lock 2")" }, { depth: 5, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: 5, monitor: "NamedLock("Lock 2")" }, { depth: 5, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 1, monitor: "class art.Test1922$Target" }, { depth: 5, monitor: "NamedLock("Lock 2")" }, { depth: 5, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 1, monitor: "class art.Test1922$Target" }, { depth: 5, monitor: "NamedLock("Lock 2")" }, { depth: 5, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 3, monitor: "class art.Test1922$Target" }, { depth: 5, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 1, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 1, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 1, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 3, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 1, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 1, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 3, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 1, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 3, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockSync, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+owner-monitors-stack-depth, suspend, other thread
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockOther]
+Owned monitors: [{ depth: 2, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra]
+Owned monitors: [{ depth: 2, monitor: "NamedLock("Lock 2")" }, { depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 4, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 4, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 4, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 4, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 4, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [{ depth: 2, monitor: "NamedLock("Lock 2")" }, { depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 4, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [{ depth: 2, monitor: "NamedLock("Lock 2")" }, { depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 4, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 4, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 4, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 4, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 4, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockSync, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
diff --git a/test/1922-owned-monitors-info/info.txt b/test/1922-owned-monitors-info/info.txt
new file mode 100644
index 0000000..c8c9189
--- /dev/null
+++ b/test/1922-owned-monitors-info/info.txt
@@ -0,0 +1,3 @@
+Tests basic functions in the jvmti plugin.
+
+Tests that the GetBytecodes function works as expected.
diff --git a/test/1922-owned-monitors-info/owned_monitors.cc b/test/1922-owned-monitors-info/owned_monitors.cc
new file mode 100644
index 0000000..66a8368
--- /dev/null
+++ b/test/1922-owned-monitors-info/owned_monitors.cc
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pthread.h>
+
+#include <cstdio>
+#include <iostream>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "jni.h"
+#include "jvmti.h"
+
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1922OwnedMonitors {
+
+static bool doMonitorEnter(JNIEnv* env, jobject target) {
+ return env->MonitorEnter(target) != 0;
+}
+static bool doMonitorExit(JNIEnv* env, jobject target) {
+ return env->MonitorExit(target) != 0;
+}
+
+static bool doCallRunnable(JNIEnv* env, jobject next) {
+ ScopedLocalRef<jclass> run_class(env, env->FindClass("java/lang/Runnable"));
+ if (run_class.get() == nullptr) {
+ return true;
+ }
+ jmethodID run = env->GetMethodID(run_class.get(), "run", "()V");
+ if (env->ExceptionCheck()) {
+ return true;
+ }
+ env->CallVoidMethod(next, run);
+ return env->ExceptionCheck();
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1922_00024Target_lockThisNative(
+ JNIEnv* env, jobject thiz, jobject next) {
+ if (doMonitorEnter(env, thiz)) {
+ return;
+ }
+ if (doCallRunnable(env, next)) {
+ return;
+ }
+ if (doMonitorExit(env, thiz)) {
+ return;
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1922_00024Target_lockNative(
+ JNIEnv* env, jobject thiz ATTRIBUTE_UNUSED, jobject mon, jobject next) {
+ if (doMonitorEnter(env, mon)) {
+ return;
+ }
+ if (doCallRunnable(env, next)) {
+ return;
+ }
+ if (doMonitorExit(env, mon)) {
+ return;
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1922_setupTest(JNIEnv* env, jclass) {
+ jvmtiCapabilities caps;
+ memset(&caps, 0, sizeof(caps));
+ caps.can_get_owned_monitor_info = 1;
+ caps.can_get_owned_monitor_stack_depth_info = 1;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps));
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test1922_getOwnedMonitorStackDepthInfo(
+ JNIEnv* env, jclass, jthread thread) {
+ jint len = 0;
+ jvmtiMonitorStackDepthInfo* monitors = nullptr;
+ if (JvmtiErrorToException(
+ env, jvmti_env, jvmti_env->GetOwnedMonitorStackDepthInfo(thread, &len, &monitors))) {
+ return nullptr;
+ }
+ ScopedLocalRef<jclass> ret_class(env, env->FindClass("art/Test1922$MonitorStackDepthInfo"));
+ if (ret_class.get() == nullptr) {
+ // CNFE should be pending.
+ return nullptr;
+ }
+ jmethodID constructor = env->GetMethodID(ret_class.get(), "<init>", "(ILjava/lang/Object;)V");
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ return CreateObjectArray(env, len, "art/Test1922$MonitorStackDepthInfo",
+ [&](jint i) {
+ return env->NewObject(ret_class.get(),
+ constructor,
+ monitors[i].stack_depth,
+ monitors[i].monitor);
+ });
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test1922_getOwnedMonitors(JNIEnv* env,
+ jclass,
+ jthread thread) {
+ jint len = 0;
+ jobject* arr = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetOwnedMonitorInfo(thread, &len, &arr))) {
+ return nullptr;
+ }
+ ScopedLocalRef<jclass> obj_class(env, env->FindClass("java/lang/Object"));
+ if (obj_class.get() == nullptr) {
+ // CNFE should be pending.
+ return nullptr;
+ }
+ jobjectArray ret = env->NewObjectArray(len, obj_class.get(), nullptr);
+ if (ret == nullptr) {
+ return nullptr;
+ }
+ for (jint i = 0; i < len; i++) {
+ env->SetObjectArrayElement(ret, i, arr[i]);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ }
+ return ret;
+}
+
+} // namespace Test1922OwnedMonitors
+} // namespace art
diff --git a/test/1922-owned-monitors-info/run b/test/1922-owned-monitors-info/run
new file mode 100755
index 0000000..e92b873
--- /dev/null
+++ b/test/1922-owned-monitors-info/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/1922-owned-monitors-info/src/Main.java b/test/1922-owned-monitors-info/src/Main.java
new file mode 100644
index 0000000..2bd272f
--- /dev/null
+++ b/test/1922-owned-monitors-info/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1922.run();
+ }
+}
diff --git a/test/1922-owned-monitors-info/src/art/Suspension.java b/test/1922-owned-monitors-info/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1922-owned-monitors-info/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1922-owned-monitors-info/src/art/Test1922.java b/test/1922-owned-monitors-info/src/art/Test1922.java
new file mode 100644
index 0000000..82f1a6c
--- /dev/null
+++ b/test/1922-owned-monitors-info/src/art/Test1922.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public class Test1922 {
+ // Set to true to run with all combinations of locks. This isn't really needed for the test to be
+ // useful and fully representative.
+ public final static boolean ALL_COMBOS = false;
+
+ // A runnable that lets us know when a different thread is paused.
+ public static class ThreadPauser implements Runnable {
+ private boolean suspend;
+ private volatile Thread paused_thread;
+ public ThreadPauser(boolean suspend) {
+ paused_thread = null;
+ this.suspend = suspend;
+ }
+ public ThreadPauser() {
+ paused_thread = null;
+ this.suspend = false;
+ }
+
+ @Override
+ public void run() {
+ this.paused_thread = Thread.currentThread();
+ if (suspend) {
+ Suspension.suspend(paused_thread);
+ }
+ while (this.paused_thread != null) {}
+ }
+
+ public void waitForOtherThreadToPause() {
+ while (this.paused_thread == null) {}
+ if (suspend) {
+ while (!Suspension.isSuspended(this.paused_thread)) {}
+ }
+ }
+
+ public void wakeupOtherThread() {
+ if (this.paused_thread == null) {
+ throw new Error("Other thread is not paused!");
+ }
+ if (suspend) {
+ Suspension.resume(this.paused_thread);
+ while (Suspension.isSuspended(this.paused_thread)) {}
+ }
+ this.paused_thread = null;
+ }
+ }
+
+ // A class with a number of monitor operations in its methods.
+ public static class Target {
+ public String name;
+ public Target(String name) { this.name = name; }
+ public String toString() { return "Target(\"" + name + "\")"; }
+
+ // synchronize on Target.class
+ public void lockClass(Runnable safepoint) {
+ synchronized (this.getClass()) {
+ safepoint.run();
+ }
+ }
+
+ // use java synchronized method.
+ public synchronized void lockSync(Runnable safepoint) {
+ safepoint.run();
+ }
+
+ // use java synchronized method and synchronize on another object.
+ public synchronized void lockExtra(Object l, Runnable safepoint) {
+ synchronized (l) {
+ safepoint.run();
+ }
+ }
+
+ // monitor enter the object 'l' in native code.
+ public native void lockNative(Object l, Runnable safepoint);
+
+ // monitor enter 'this' in native code.
+ public native void lockThisNative(Runnable safepoint);
+
+ // synchronize on 'l'
+ public void lockOther(Object l, Runnable safepoint) {
+ synchronized (l) {
+ safepoint.run();
+ }
+ }
+
+ // Don't do anything. Just call the next method.
+ public void callSafepoint(Runnable safepoint) {
+ safepoint.run();
+ }
+ }
+
+ // A lock with a toString.
+ public static class NamedLock {
+ public String name;
+ public NamedLock(String name) { this.name = name; }
+ public String toString() { return "NamedLock(\"" + name + "\")"; }
+ }
+
+ private static Object[] sortByString(Object[] arr) {
+ Arrays.sort(arr, (a, b) -> a.toString().compareTo(b.toString()));
+ return arr;
+ }
+
+ public static class PrintOwnedMonitorsStackDepthRunnable implements Runnable {
+ public final Thread target;
+ public PrintOwnedMonitorsStackDepthRunnable(Thread target) {
+ this.target = target;
+ }
+ public void run() {
+ System.out.println("Owned monitors: " +
+ Arrays.toString(sortByString(getOwnedMonitorStackDepthInfo(target))));
+ }
+ }
+
+ public static class PrintOwnedMonitorsRunnable implements Runnable {
+ public final Thread target;
+ public PrintOwnedMonitorsRunnable(Thread target) {
+ this.target = target;
+ }
+ public void run() {
+ System.out.println("Owned monitors: " +
+ Arrays.toString(sortByString(getOwnedMonitors(target))));
+ }
+ }
+
+ public static void run() throws Exception {
+ setupTest();
+
+ System.out.println("owner-monitors, This thread");
+ runTestsCurrentThread("owned-monitor",
+ new PrintOwnedMonitorsRunnable(Thread.currentThread()));
+
+ System.out.println("owner-monitors, no suspend, Other thread");
+ runTestsOtherThread("owned-monitor", false,
+ (t) -> { new PrintOwnedMonitorsRunnable(t).run(); });
+
+ System.out.println("owner-monitors, suspend, Other thread");
+ runTestsOtherThread("owned-monitor", true,
+ (t) -> { new PrintOwnedMonitorsRunnable(t).run(); });
+
+ System.out.println("owner-monitors-stack-depth, This thread");
+ runTestsCurrentThread("owned-stack-depth",
+ new PrintOwnedMonitorsStackDepthRunnable(Thread.currentThread()));
+
+ System.out.println("owner-monitors-stack-depth, no suspend, other thread");
+ runTestsOtherThread("owned-stack-depth", false,
+ (t) -> { new PrintOwnedMonitorsStackDepthRunnable(t).run(); });
+
+ System.out.println("owner-monitors-stack-depth, suspend, other thread");
+ runTestsOtherThread("owned-stack-depth", true,
+ (t) -> { new PrintOwnedMonitorsStackDepthRunnable(t).run(); });
+ }
+
+ public static void runTestsOtherThread(String name, boolean suspend, Consumer<Thread> printer) {
+ final Target t = new Target("Other thread test (suspend: " + suspend + "): " + name);
+ final NamedLock l1 = new NamedLock("Lock 1");
+ final NamedLock l2 = new NamedLock("Lock 2");
+ final NamedLock l3 = new NamedLock("Lock 3");
+
+ List<Function<Runnable, Runnable>> MkSafepoints = Arrays.asList(
+ (r) -> { return new CallLockOther(t, l1, r); },
+ (r) -> { return new CallLockExtra(t, l2, r); },
+ (r) -> { return new CallLockNative(t, l3, r); },
+ (r) -> { return new CallLockThisNative(t, r); },
+ (r) -> { return new CallLockClass(t, r); },
+ (r) -> { return new CallLockSync(t, r); },
+ (r) -> { return new CallSafepoint(t, r); }
+ );
+ // Use ListIterators so we can have elements in the test multiple times.
+ ListIterator<Function<Runnable, Runnable>> li1 = MkSafepoints.listIterator();
+ for (Function<Runnable, Runnable> r1 = li1.next(); li1.hasNext(); r1 = li1.next()) {
+ ListIterator<Function<Runnable, Runnable>> li2 =
+ MkSafepoints.listIterator(ALL_COMBOS ? 0 : li1.previousIndex());
+ for (Function<Runnable, Runnable> r2 = li2.next(); li2.hasNext(); r2 = li2.next()) {
+ ListIterator<Function<Runnable, Runnable>> li3 =
+ MkSafepoints.listIterator(ALL_COMBOS ? 0 : li2.previousIndex());
+ for (Function<Runnable, Runnable> r3 = li3.next(); li3.hasNext(); r3 = li3.next()) {
+ System.out.println("Running: " + Arrays.toString(
+ new Object[] {
+ r1.apply(null).getClass(),
+ r2.apply(null).getClass(),
+ r3.apply(null).getClass(),
+ }));
+ try {
+ final ThreadPauser pause = new ThreadPauser(suspend);
+ final Thread thr = new Thread(r1.apply(r2.apply(r3.apply(pause))));
+ thr.start();
+ pause.waitForOtherThreadToPause();
+ printer.accept(thr);
+ pause.wakeupOtherThread();
+ thr.join();
+ } catch (Exception e) {
+ throw new Error("Exception in test." , e);
+ }
+ }
+ }
+ }
+ }
+ public static void runTestsCurrentThread(String name, Runnable printer) {
+ final Target t = new Target("Current thread test: " + name);
+ final NamedLock l1 = new NamedLock("Lock 1");
+ final NamedLock l2 = new NamedLock("Lock 2");
+ final NamedLock l3 = new NamedLock("Lock 3");
+
+ List<Function<Runnable, Runnable>> MkSafepoints = Arrays.asList(
+ (r) -> { return new CallLockOther(t, l1, r); },
+ (r) -> { return new CallLockExtra(t, l2, r); },
+ (r) -> { return new CallLockNative(t, l3, r); },
+ (r) -> { return new CallLockThisNative(t, r); },
+ (r) -> { return new CallLockClass(t, r); },
+ (r) -> { return new CallLockSync(t, r); },
+ (r) -> { return new CallSafepoint(t, r); }
+ );
+ ListIterator<Function<Runnable, Runnable>> li1 = MkSafepoints.listIterator();
+ for (Function<Runnable, Runnable> r1 = li1.next(); li1.hasNext(); r1 = li1.next()) {
+ ListIterator<Function<Runnable, Runnable>> li2 =
+ MkSafepoints.listIterator(ALL_COMBOS ? 0 : li1.previousIndex());
+ for (Function<Runnable, Runnable> r2 = li2.next(); li2.hasNext(); r2 = li2.next()) {
+ ListIterator<Function<Runnable, Runnable>> li3 =
+ MkSafepoints.listIterator(ALL_COMBOS ? 0 : li2.previousIndex());
+ for (Function<Runnable, Runnable> r3 = li3.next(); li3.hasNext(); r3 = li3.next()) {
+ System.out.println("Running: " + Arrays.toString(
+ new Object[] {
+ r1.apply(null).getClass(),
+ r2.apply(null).getClass(),
+ r3.apply(null).getClass(),
+ }));
+ r1.apply(r2.apply(r3.apply(printer))).run();
+ }
+ }
+ }
+ }
+
+ public static native void setupTest();
+ public static native Object[] getOwnedMonitors(Thread thr);
+ public static native MonitorStackDepthInfo[] getOwnedMonitorStackDepthInfo(Thread thr);
+ public static class MonitorStackDepthInfo {
+ public final int depth;
+ public final Object monitor;
+ public MonitorStackDepthInfo(int depth, Object monitor) {
+ this.depth = depth;
+ this.monitor = monitor;
+ }
+ public String toString() {
+ return "{ depth: " + depth + ", monitor: \"" + monitor.toString() + "\" }";
+ }
+ }
+
+ // We want to avoid synthetic methods that would mess up our stack-depths so we make everything
+ // explicit here.
+ public static class CallSafepoint implements Runnable {
+ public final Target target;
+ public final Runnable safepoint;
+ public CallSafepoint(Target target, Runnable safepoint) {
+ this.target = target;
+ this.safepoint = safepoint;
+ }
+ public void run() {
+ target.callSafepoint(safepoint);
+ }
+ }
+ public static class CallLockOther implements Runnable {
+ public final Target target;
+ public final Object l;
+ public final Runnable safepoint;
+ public CallLockOther(Target target, Object l, Runnable safepoint) {
+ this.target = target;
+ this.l = l;
+ this.safepoint = safepoint;
+ }
+ public void run() {
+ target.lockOther(l, safepoint);
+ }
+ }
+ public static class CallLockExtra implements Runnable {
+ public final Target target;
+ public final Object l;
+ public final Runnable safepoint;
+ public CallLockExtra(Target target, Object l, Runnable safepoint) {
+ this.target = target;
+ this.l = l;
+ this.safepoint = safepoint;
+ }
+ public void run() {
+ target.lockExtra(l, safepoint);
+ }
+ }
+ public static class CallLockThisNative implements Runnable {
+ public final Target target;
+ public final Runnable safepoint;
+ public CallLockThisNative(Target target, Runnable safepoint) {
+ this.target = target;
+ this.safepoint = safepoint;
+ }
+ public void run() {
+ target.lockThisNative(safepoint);
+ }
+ }
+ public static class CallLockNative implements Runnable {
+ public final Target target;
+ public final Object l;
+ public final Runnable safepoint;
+ public CallLockNative(Target target, Object l, Runnable safepoint) {
+ this.target = target;
+ this.l = l;
+ this.safepoint = safepoint;
+ }
+ public void run() {
+ target.lockNative(l, safepoint);
+ }
+ }
+ public static class CallLockClass implements Runnable {
+ public final Target target;
+ public final Runnable safepoint;
+ public CallLockClass(Target target, Runnable safepoint) {
+ this.target = target;
+ this.safepoint = safepoint;
+ }
+ public void run() {
+ target.lockClass(safepoint);
+ }
+ }
+ public static class CallLockSync implements Runnable {
+ public final Target target;
+ public final Runnable safepoint;
+ public CallLockSync(Target target, Runnable safepoint) {
+ this.target = target;
+ this.safepoint = safepoint;
+ }
+ public void run() {
+ target.lockSync(safepoint);
+ }
+ }
+}
diff --git a/test/1923-frame-pop/expected.txt b/test/1923-frame-pop/expected.txt
new file mode 100644
index 0000000..7bc001d
--- /dev/null
+++ b/test/1923-frame-pop/expected.txt
@@ -0,0 +1,8 @@
+public static void art.Test1923.recurTimesA(int,java.lang.Runnable) pop. Line=44 exception:false
+Ran recurTimes(10) without errors!
+public static void art.Test1923.recurTimesF(int,java.lang.Runnable) pop. Line=84 exception:false
+Ran recurTimes(10) without errors!
+public static void art.Test1923.recurTimesK(int,java.lang.Runnable) pop. Line=124 exception:false
+Ran recurTimes(10) without errors!
+public static void art.Test1923.recurTimesF(int,java.lang.Runnable) pop. Line=83 exception:true
+Caught exception art.Test1923$RecursionError: Unable recur further. Still 90 outstanding! while running recurTimes(100)
diff --git a/test/1923-frame-pop/info.txt b/test/1923-frame-pop/info.txt
new file mode 100644
index 0000000..b4984d9
--- /dev/null
+++ b/test/1923-frame-pop/info.txt
@@ -0,0 +1,3 @@
+Tests notify frame pop JVMTI functionality.
+
+This tests the normal use case.
diff --git a/test/1923-frame-pop/run b/test/1923-frame-pop/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1923-frame-pop/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/1923-frame-pop/src/Main.java b/test/1923-frame-pop/src/Main.java
new file mode 100644
index 0000000..8881483
--- /dev/null
+++ b/test/1923-frame-pop/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1923.run();
+ }
+}
diff --git a/test/1923-frame-pop/src/art/Breakpoint.java b/test/1923-frame-pop/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1923-frame-pop/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/1923-frame-pop/src/art/FramePop.java b/test/1923-frame-pop/src/art/FramePop.java
new file mode 100644
index 0000000..86bf226
--- /dev/null
+++ b/test/1923-frame-pop/src/art/FramePop.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Method;
+
+public class FramePop {
+ public static native void enableFramePopEvent(Class klass, Method method, Thread thr)
+ throws Exception;
+ public static native void notifyFramePop(Thread target, int depth) throws Exception;
+}
diff --git a/test/1923-frame-pop/src/art/Locals.java b/test/1923-frame-pop/src/art/Locals.java
new file mode 100644
index 0000000..22e21be
--- /dev/null
+++ b/test/1923-frame-pop/src/art/Locals.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.Objects;
+
+public class Locals {
+ public static native void EnableLocalVariableAccess();
+
+ public static class VariableDescription {
+ public final long start_location;
+ public final int length;
+ public final String name;
+ public final String signature;
+ public final String generic_signature;
+ public final int slot;
+
+ public VariableDescription(
+ long start, int length, String name, String sig, String gen_sig, int slot) {
+ this.start_location = start;
+ this.length = length;
+ this.name = name;
+ this.signature = sig;
+ this.generic_signature = gen_sig;
+ this.slot = slot;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "VariableDescription { " +
+ "Sig: '%s', Name: '%s', Gen_sig: '%s', slot: %d, start: %d, len: %d" +
+ "}",
+ this.signature,
+ this.name,
+ this.generic_signature,
+ this.slot,
+ this.start_location,
+ this.length);
+ }
+ public boolean equals(Object other) {
+ if (!(other instanceof VariableDescription)) {
+ return false;
+ } else {
+ VariableDescription v = (VariableDescription)other;
+ return Objects.equals(v.signature, signature) &&
+ Objects.equals(v.name, name) &&
+ Objects.equals(v.generic_signature, generic_signature) &&
+ v.slot == slot &&
+ v.start_location == start_location &&
+ v.length == length;
+ }
+ }
+ public int hashCode() {
+ return Objects.hash(this.signature, this.name, this.generic_signature, this.slot,
+ this.start_location, this.length);
+ }
+ }
+
+ public static native VariableDescription[] GetLocalVariableTable(Executable e);
+
+ public static VariableDescription GetVariableAtLine(
+ Executable e, String name, String sig, int line) throws Exception {
+ return GetVariableAtLocation(e, name, sig, Breakpoint.lineToLocation(e, line));
+ }
+
+ public static VariableDescription GetVariableAtLocation(
+ Executable e, String name, String sig, long loc) {
+ VariableDescription[] vars = GetLocalVariableTable(e);
+ for (VariableDescription var : vars) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(name) &&
+ var.signature.equals(sig)) {
+ return var;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + name + " (sig: " + sig + ") in " + e + " at loc " + loc);
+ }
+
+ public static native int GetLocalVariableInt(Thread thr, int depth, int slot);
+ public static native long GetLocalVariableLong(Thread thr, int depth, int slot);
+ public static native float GetLocalVariableFloat(Thread thr, int depth, int slot);
+ public static native double GetLocalVariableDouble(Thread thr, int depth, int slot);
+ public static native Object GetLocalVariableObject(Thread thr, int depth, int slot);
+ public static native Object GetLocalInstance(Thread thr, int depth);
+
+ public static void SetLocalVariableInt(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableInt(thr, depth, slot, ((Number)val).intValue());
+ }
+ public static void SetLocalVariableLong(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableLong(thr, depth, slot, ((Number)val).longValue());
+ }
+ public static void SetLocalVariableFloat(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableFloat(thr, depth, slot, ((Number)val).floatValue());
+ }
+ public static void SetLocalVariableDouble(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableDouble(thr, depth, slot, ((Number)val).doubleValue());
+ }
+ public static native void SetLocalVariableInt(Thread thr, int depth, int slot, int val);
+ public static native void SetLocalVariableLong(Thread thr, int depth, int slot, long val);
+ public static native void SetLocalVariableFloat(Thread thr, int depth, int slot, float val);
+ public static native void SetLocalVariableDouble(Thread thr, int depth, int slot, double val);
+ public static native void SetLocalVariableObject(Thread thr, int depth, int slot, Object val);
+}
diff --git a/test/1923-frame-pop/src/art/StackTrace.java b/test/1923-frame-pop/src/art/StackTrace.java
new file mode 100644
index 0000000..2ea2f20
--- /dev/null
+++ b/test/1923-frame-pop/src/art/StackTrace.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Executable;
+
+public class StackTrace {
+ public static class StackFrameData {
+ public final Thread thr;
+ public final Executable method;
+ public final long current_location;
+ public final int depth;
+
+ public StackFrameData(Thread thr, Executable e, long loc, int depth) {
+ this.thr = thr;
+ this.method = e;
+ this.current_location = loc;
+ this.depth = depth;
+ }
+ @Override
+ public String toString() {
+ return String.format(
+ "StackFrameData { thr: '%s', method: '%s', loc: %d, depth: %d }",
+ this.thr,
+ this.method,
+ this.current_location,
+ this.depth);
+ }
+ }
+
+ public static native int GetStackDepth(Thread thr);
+
+ private static native StackFrameData[] nativeGetStackTrace(Thread thr);
+
+ public static StackFrameData[] GetStackTrace(Thread thr) {
+ // The RI seems to give inconsistent (and sometimes nonsensical) results if the thread is not
+ // suspended. The spec says that not being suspended is fine but since we want this to be
+ // consistent we will suspend for the RI.
+ boolean suspend_thread =
+ !System.getProperty("java.vm.name").equals("Dalvik") &&
+ !thr.equals(Thread.currentThread()) &&
+ !Suspension.isSuspended(thr);
+ if (suspend_thread) {
+ Suspension.suspend(thr);
+ }
+ StackFrameData[] out = nativeGetStackTrace(thr);
+ if (suspend_thread) {
+ Suspension.resume(thr);
+ }
+ return out;
+ }
+}
+
diff --git a/test/1923-frame-pop/src/art/Suspension.java b/test/1923-frame-pop/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1923-frame-pop/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1923-frame-pop/src/art/Test1923.java b/test/1923-frame-pop/src/art/Test1923.java
new file mode 100644
index 0000000..b5265b7
--- /dev/null
+++ b/test/1923-frame-pop/src/art/Test1923.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.concurrent.Semaphore;
+import java.util.Arrays;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.function.IntUnaryOperator;
+import java.util.function.Function;
+
+public class Test1923 {
+ public static void handleFramePop(Executable m, boolean exception, long location) {
+ System.out.println(
+ m + " pop. Line=" + Breakpoint.locationToLine(m, location) + " exception:" + exception);
+ }
+
+ public static void recurTimesA(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesB(times - 1, safepoint);
+ }
+
+ public static void recurTimesB(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesC(times - 1, safepoint);
+ }
+
+ public static void recurTimesC(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesD(times - 1, safepoint);
+ }
+
+ public static void recurTimesD(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesE(times - 1, safepoint);
+ }
+
+ public static void recurTimesE(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesF(times - 1, safepoint);
+ }
+
+ public static void recurTimesF(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesG(times - 1, safepoint);
+ }
+
+ public static void recurTimesG(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesH(times - 1, safepoint);
+ }
+
+ public static void recurTimesH(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesI(times - 1, safepoint);
+ }
+
+ public static void recurTimesI(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesJ(times - 1, safepoint);
+ }
+
+ public static void recurTimesJ(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesK(times - 1, safepoint);
+ }
+
+ public static class RecursionError extends Error {
+ public RecursionError(String s) { super(s); }
+ }
+ public static void recurTimesK(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ safepoint.run();
+ throw new RecursionError("Unable recur further. Still " + times + " outstanding!");
+ }
+
+ public static class ThreadPauser implements Runnable {
+ public final Semaphore sem_wakeup_main;
+ public final Semaphore sem_wait;
+
+ public ThreadPauser() {
+ sem_wakeup_main = new Semaphore(0);
+ sem_wait = new Semaphore(0);
+ }
+
+ public void run() {
+ try {
+ sem_wakeup_main.release();
+ sem_wait.acquire();
+ } catch (Exception e) {
+ throw new Error("Error with semaphores!", e);
+ }
+ }
+
+ public void waitForOtherThreadToPause() throws Exception {
+ sem_wakeup_main.acquire();
+ }
+
+ public void wakeupOtherThread() throws Exception {
+ sem_wait.release();
+ }
+ }
+
+ public static void doRecurTestWith(final int times, int watch_frame) throws Exception {
+ final String target_method_name_start = "recurTimes";
+ final ThreadPauser safepoint = new ThreadPauser();
+ Thread target = new Thread(() -> {
+ try {
+ recurTimesA(times, safepoint);
+ System.out.println("Ran recurTimes(" + times + ") without errors!");
+ } catch (RecursionError e) {
+ System.out.println("Caught exception " + e + " while running recurTimes(" + times + ")");
+ }
+ });
+ target.start();
+ safepoint.waitForOtherThreadToPause();
+ Suspension.suspend(target);
+ // Safe block
+ int cnt = 0;
+ StackTrace.StackFrameData target_frame = null;
+ for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(target)) {
+ if (frame.method.getName().startsWith(target_method_name_start)) {
+ if (times - cnt == watch_frame) {
+ target_frame = frame;
+ break;
+ } else {
+ cnt++;
+ }
+ }
+ }
+ if (target_frame != null) {
+ FramePop.notifyFramePop(target, target_frame.depth);
+ } else {
+ System.out.println(
+ "Unable to find stack frame for " + watch_frame + " depth of "
+ + target_method_name_start);
+ }
+ Suspension.resume(target);
+ safepoint.wakeupOtherThread();
+ target.join();
+ }
+
+ public static void run() throws Exception {
+ // TODO Investigate what thread argument means for FramePop event enable.
+ // Listen for events on all threads.
+ FramePop.enableFramePopEvent(
+ Test1923.class,
+ Test1923.class.getDeclaredMethod(
+ "handleFramePop", Executable.class, Boolean.TYPE, Long.TYPE),
+ null);
+ doRecurTestWith(10, 0);
+ doRecurTestWith(10, 5);
+ doRecurTestWith(10, 10);
+ doRecurTestWith(100, 95);
+ }
+}
diff --git a/test/1923-frame-pop/src/art/Trace.java b/test/1923-frame-pop/src/art/Trace.java
new file mode 100644
index 0000000..ba3d397
--- /dev/null
+++ b/test/1923-frame-pop/src/art/Trace.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class Trace {
+ public static native void enableTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Method singleStep,
+ Thread thr);
+ public static native void disableTracing(Thread thr);
+
+ public static void enableFieldTracing(Class<?> methodClass,
+ Method fieldAccess,
+ Method fieldModify,
+ Thread thr) {
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
+ }
+
+ public static void enableMethodTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Thread thr) {
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+ }
+
+ public static void enableSingleStepTracing(Class<?> methodClass,
+ Method singleStep,
+ Thread thr) {
+ enableTracing(methodClass, null, null, null, null, singleStep, thr);
+ }
+
+ public static native void watchFieldAccess(Field f);
+ public static native void watchFieldModification(Field f);
+ public static native void watchAllFieldAccesses();
+ public static native void watchAllFieldModifications();
+}
diff --git a/test/1924-frame-pop-toggle/expected.txt b/test/1924-frame-pop-toggle/expected.txt
new file mode 100644
index 0000000..64ca9db
--- /dev/null
+++ b/test/1924-frame-pop-toggle/expected.txt
@@ -0,0 +1,8 @@
+public static void art.Test1924.recurTimesA(int,java.lang.Runnable) pop. Line=44 exception:false
+Ran recurTimes(10) without errors!
+public static void art.Test1924.recurTimesF(int,java.lang.Runnable) pop. Line=84 exception:false
+Ran recurTimes(10) without errors!
+public static void art.Test1924.recurTimesK(int,java.lang.Runnable) pop. Line=124 exception:false
+Ran recurTimes(10) without errors!
+public static void art.Test1924.recurTimesF(int,java.lang.Runnable) pop. Line=83 exception:true
+Caught exception art.Test1924$RecursionError: Unable recur further. Still 90 outstanding! while running recurTimes(100)
diff --git a/test/1924-frame-pop-toggle/frame_pop_toggle.cc b/test/1924-frame-pop-toggle/frame_pop_toggle.cc
new file mode 100644
index 0000000..1fb7a1f
--- /dev/null
+++ b/test/1924-frame-pop-toggle/frame_pop_toggle.cc
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pthread.h>
+
+#include <cstdio>
+#include <iostream>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "jni.h"
+#include "jvmti.h"
+
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1924FramePop {
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1924_toggleFramePop(
+ JNIEnv* env, jclass, jthread thr) {
+ JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(
+ JVMTI_DISABLE, JVMTI_EVENT_FRAME_POP, thr));
+ JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(
+ JVMTI_ENABLE, JVMTI_EVENT_FRAME_POP, thr));
+}
+
+} // namespace Test1924FramePop
+} // namespace art
+
diff --git a/test/1924-frame-pop-toggle/info.txt b/test/1924-frame-pop-toggle/info.txt
new file mode 100644
index 0000000..1f7480f
--- /dev/null
+++ b/test/1924-frame-pop-toggle/info.txt
@@ -0,0 +1,3 @@
+Tests notify frame pop JVMTI functionality.
+
+This tests toggling frame pop off and on.
diff --git a/test/1924-frame-pop-toggle/run b/test/1924-frame-pop-toggle/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1924-frame-pop-toggle/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/1924-frame-pop-toggle/src/Main.java b/test/1924-frame-pop-toggle/src/Main.java
new file mode 100644
index 0000000..f48fc2d
--- /dev/null
+++ b/test/1924-frame-pop-toggle/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1924.run();
+ }
+}
diff --git a/test/1924-frame-pop-toggle/src/art/Breakpoint.java b/test/1924-frame-pop-toggle/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1924-frame-pop-toggle/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/1924-frame-pop-toggle/src/art/FramePop.java b/test/1924-frame-pop-toggle/src/art/FramePop.java
new file mode 100644
index 0000000..86bf226
--- /dev/null
+++ b/test/1924-frame-pop-toggle/src/art/FramePop.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Method;
+
+public class FramePop {
+ public static native void enableFramePopEvent(Class klass, Method method, Thread thr)
+ throws Exception;
+ public static native void notifyFramePop(Thread target, int depth) throws Exception;
+}
diff --git a/test/1924-frame-pop-toggle/src/art/Locals.java b/test/1924-frame-pop-toggle/src/art/Locals.java
new file mode 100644
index 0000000..22e21be
--- /dev/null
+++ b/test/1924-frame-pop-toggle/src/art/Locals.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.Objects;
+
+public class Locals {
+ public static native void EnableLocalVariableAccess();
+
+ public static class VariableDescription {
+ public final long start_location;
+ public final int length;
+ public final String name;
+ public final String signature;
+ public final String generic_signature;
+ public final int slot;
+
+ public VariableDescription(
+ long start, int length, String name, String sig, String gen_sig, int slot) {
+ this.start_location = start;
+ this.length = length;
+ this.name = name;
+ this.signature = sig;
+ this.generic_signature = gen_sig;
+ this.slot = slot;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "VariableDescription { " +
+ "Sig: '%s', Name: '%s', Gen_sig: '%s', slot: %d, start: %d, len: %d" +
+ "}",
+ this.signature,
+ this.name,
+ this.generic_signature,
+ this.slot,
+ this.start_location,
+ this.length);
+ }
+ public boolean equals(Object other) {
+ if (!(other instanceof VariableDescription)) {
+ return false;
+ } else {
+ VariableDescription v = (VariableDescription)other;
+ return Objects.equals(v.signature, signature) &&
+ Objects.equals(v.name, name) &&
+ Objects.equals(v.generic_signature, generic_signature) &&
+ v.slot == slot &&
+ v.start_location == start_location &&
+ v.length == length;
+ }
+ }
+ public int hashCode() {
+ return Objects.hash(this.signature, this.name, this.generic_signature, this.slot,
+ this.start_location, this.length);
+ }
+ }
+
+ public static native VariableDescription[] GetLocalVariableTable(Executable e);
+
+ public static VariableDescription GetVariableAtLine(
+ Executable e, String name, String sig, int line) throws Exception {
+ return GetVariableAtLocation(e, name, sig, Breakpoint.lineToLocation(e, line));
+ }
+
+ public static VariableDescription GetVariableAtLocation(
+ Executable e, String name, String sig, long loc) {
+ VariableDescription[] vars = GetLocalVariableTable(e);
+ for (VariableDescription var : vars) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(name) &&
+ var.signature.equals(sig)) {
+ return var;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + name + " (sig: " + sig + ") in " + e + " at loc " + loc);
+ }
+
+ public static native int GetLocalVariableInt(Thread thr, int depth, int slot);
+ public static native long GetLocalVariableLong(Thread thr, int depth, int slot);
+ public static native float GetLocalVariableFloat(Thread thr, int depth, int slot);
+ public static native double GetLocalVariableDouble(Thread thr, int depth, int slot);
+ public static native Object GetLocalVariableObject(Thread thr, int depth, int slot);
+ public static native Object GetLocalInstance(Thread thr, int depth);
+
+ public static void SetLocalVariableInt(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableInt(thr, depth, slot, ((Number)val).intValue());
+ }
+ public static void SetLocalVariableLong(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableLong(thr, depth, slot, ((Number)val).longValue());
+ }
+ public static void SetLocalVariableFloat(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableFloat(thr, depth, slot, ((Number)val).floatValue());
+ }
+ public static void SetLocalVariableDouble(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableDouble(thr, depth, slot, ((Number)val).doubleValue());
+ }
+ public static native void SetLocalVariableInt(Thread thr, int depth, int slot, int val);
+ public static native void SetLocalVariableLong(Thread thr, int depth, int slot, long val);
+ public static native void SetLocalVariableFloat(Thread thr, int depth, int slot, float val);
+ public static native void SetLocalVariableDouble(Thread thr, int depth, int slot, double val);
+ public static native void SetLocalVariableObject(Thread thr, int depth, int slot, Object val);
+}
diff --git a/test/1924-frame-pop-toggle/src/art/StackTrace.java b/test/1924-frame-pop-toggle/src/art/StackTrace.java
new file mode 100644
index 0000000..2ea2f20
--- /dev/null
+++ b/test/1924-frame-pop-toggle/src/art/StackTrace.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Executable;
+
+public class StackTrace {
+ public static class StackFrameData {
+ public final Thread thr;
+ public final Executable method;
+ public final long current_location;
+ public final int depth;
+
+ public StackFrameData(Thread thr, Executable e, long loc, int depth) {
+ this.thr = thr;
+ this.method = e;
+ this.current_location = loc;
+ this.depth = depth;
+ }
+ @Override
+ public String toString() {
+ return String.format(
+ "StackFrameData { thr: '%s', method: '%s', loc: %d, depth: %d }",
+ this.thr,
+ this.method,
+ this.current_location,
+ this.depth);
+ }
+ }
+
+ public static native int GetStackDepth(Thread thr);
+
+ private static native StackFrameData[] nativeGetStackTrace(Thread thr);
+
+ public static StackFrameData[] GetStackTrace(Thread thr) {
+ // The RI seems to give inconsistent (and sometimes nonsensical) results if the thread is not
+ // suspended. The spec says that not being suspended is fine but since we want this to be
+ // consistent we will suspend for the RI.
+ boolean suspend_thread =
+ !System.getProperty("java.vm.name").equals("Dalvik") &&
+ !thr.equals(Thread.currentThread()) &&
+ !Suspension.isSuspended(thr);
+ if (suspend_thread) {
+ Suspension.suspend(thr);
+ }
+ StackFrameData[] out = nativeGetStackTrace(thr);
+ if (suspend_thread) {
+ Suspension.resume(thr);
+ }
+ return out;
+ }
+}
+
diff --git a/test/1924-frame-pop-toggle/src/art/Suspension.java b/test/1924-frame-pop-toggle/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1924-frame-pop-toggle/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1924-frame-pop-toggle/src/art/Test1924.java b/test/1924-frame-pop-toggle/src/art/Test1924.java
new file mode 100644
index 0000000..0ffbfc6
--- /dev/null
+++ b/test/1924-frame-pop-toggle/src/art/Test1924.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.concurrent.Semaphore;
+import java.util.Arrays;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.function.IntUnaryOperator;
+import java.util.function.Function;
+
+public class Test1924 {
+ public static void handleFramePop(Executable m, boolean exception, long location) {
+ System.out.println(
+ m + " pop. Line=" + Breakpoint.locationToLine(m, location) + " exception:" + exception);
+ }
+
+ public static void recurTimesA(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesB(times - 1, safepoint);
+ }
+
+ public static void recurTimesB(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesC(times - 1, safepoint);
+ }
+
+ public static void recurTimesC(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesD(times - 1, safepoint);
+ }
+
+ public static void recurTimesD(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesE(times - 1, safepoint);
+ }
+
+ public static void recurTimesE(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesF(times - 1, safepoint);
+ }
+
+ public static void recurTimesF(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesG(times - 1, safepoint);
+ }
+
+ public static void recurTimesG(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesH(times - 1, safepoint);
+ }
+
+ public static void recurTimesH(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesI(times - 1, safepoint);
+ }
+
+ public static void recurTimesI(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesJ(times - 1, safepoint);
+ }
+
+ public static void recurTimesJ(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesK(times - 1, safepoint);
+ }
+
+ public static class RecursionError extends Error {
+ public RecursionError(String s) { super(s); }
+ }
+ public static void recurTimesK(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ safepoint.run();
+ throw new RecursionError("Unable recur further. Still " + times + " outstanding!");
+ }
+
+ public static class ThreadPauser implements Runnable {
+ public final Semaphore sem_wakeup_main;
+ public final Semaphore sem_wait;
+
+ public ThreadPauser() {
+ sem_wakeup_main = new Semaphore(0);
+ sem_wait = new Semaphore(0);
+ }
+
+ public void run() {
+ try {
+ sem_wakeup_main.release();
+ sem_wait.acquire();
+ } catch (Exception e) {
+ throw new Error("Error with semaphores!", e);
+ }
+ }
+
+ public void waitForOtherThreadToPause() throws Exception {
+ sem_wakeup_main.acquire();
+ }
+
+ public void wakeupOtherThread() throws Exception {
+ sem_wait.release();
+ }
+ }
+
+ public static void doRecurTestWith(final int times, int watch_frame) throws Exception {
+ final String target_method_name_start = "recurTimes";
+ final ThreadPauser safepoint = new ThreadPauser();
+ Thread target = new Thread(() -> {
+ try {
+ recurTimesA(times, safepoint);
+ System.out.println("Ran recurTimes(" + times + ") without errors!");
+ } catch (RecursionError e) {
+ System.out.println("Caught exception " + e + " while running recurTimes(" + times + ")");
+ }
+ });
+ target.start();
+ safepoint.waitForOtherThreadToPause();
+ Suspension.suspend(target);
+ // Safe block
+ int cnt = 0;
+ StackTrace.StackFrameData target_frame = null;
+ for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(target)) {
+ if (frame.method.getName().startsWith(target_method_name_start)) {
+ if (times - cnt == watch_frame) {
+ target_frame = frame;
+ break;
+ } else {
+ cnt++;
+ }
+ }
+ }
+ if (target_frame != null) {
+ FramePop.notifyFramePop(target, target_frame.depth);
+ } else {
+ System.out.println(
+ "Unable to find stack frame for " + watch_frame + " depth of "
+ + target_method_name_start);
+ }
+ Suspension.resume(target);
+ toggleFramePop(null);
+ safepoint.wakeupOtherThread();
+ target.join();
+ }
+
+ public static void run() throws Exception {
+ // TODO Investigate what thread argument means for FramePop event enable.
+ // Listen for events on all threads.
+ FramePop.enableFramePopEvent(
+ Test1924.class,
+ Test1924.class.getDeclaredMethod(
+ "handleFramePop", Executable.class, Boolean.TYPE, Long.TYPE),
+ null);
+ doRecurTestWith(10, 0);
+ doRecurTestWith(10, 5);
+ doRecurTestWith(10, 10);
+ doRecurTestWith(100, 95);
+ }
+
+ public static native void toggleFramePop(Thread thr);
+}
diff --git a/test/1924-frame-pop-toggle/src/art/Trace.java b/test/1924-frame-pop-toggle/src/art/Trace.java
new file mode 100644
index 0000000..ba3d397
--- /dev/null
+++ b/test/1924-frame-pop-toggle/src/art/Trace.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class Trace {
+ public static native void enableTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Method singleStep,
+ Thread thr);
+ public static native void disableTracing(Thread thr);
+
+ public static void enableFieldTracing(Class<?> methodClass,
+ Method fieldAccess,
+ Method fieldModify,
+ Thread thr) {
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
+ }
+
+ public static void enableMethodTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Thread thr) {
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+ }
+
+ public static void enableSingleStepTracing(Class<?> methodClass,
+ Method singleStep,
+ Thread thr) {
+ enableTracing(methodClass, null, null, null, null, singleStep, thr);
+ }
+
+ public static native void watchFieldAccess(Field f);
+ public static native void watchFieldModification(Field f);
+ public static native void watchAllFieldAccesses();
+ public static native void watchAllFieldModifications();
+}
diff --git a/test/1925-self-frame-pop/expected.txt b/test/1925-self-frame-pop/expected.txt
new file mode 100644
index 0000000..f154205
--- /dev/null
+++ b/test/1925-self-frame-pop/expected.txt
@@ -0,0 +1,4 @@
+public static void art.Test1925.recurTimesE(int,java.lang.Runnable) pop. Line=76 exception:false
+Ran recurTimes(10) without errors!
+public static void art.Test1925.recurTimesE(int,java.lang.Runnable) pop. Line=75 exception:true
+Caught exception art.Test1925$RecursionError: Unable recur further. Still 90 outstanding! while running recurTimes(100)
diff --git a/test/1925-self-frame-pop/info.txt b/test/1925-self-frame-pop/info.txt
new file mode 100644
index 0000000..ccca498
--- /dev/null
+++ b/test/1925-self-frame-pop/info.txt
@@ -0,0 +1,3 @@
+Tests notify frame pop JVMTI functionality.
+
+This tests setting frame-pop on the current thread
diff --git a/test/1925-self-frame-pop/run b/test/1925-self-frame-pop/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1925-self-frame-pop/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/1925-self-frame-pop/src/Main.java b/test/1925-self-frame-pop/src/Main.java
new file mode 100644
index 0000000..2761aef
--- /dev/null
+++ b/test/1925-self-frame-pop/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1925.run();
+ }
+}
diff --git a/test/1925-self-frame-pop/src/art/Breakpoint.java b/test/1925-self-frame-pop/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1925-self-frame-pop/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/1925-self-frame-pop/src/art/FramePop.java b/test/1925-self-frame-pop/src/art/FramePop.java
new file mode 100644
index 0000000..86bf226
--- /dev/null
+++ b/test/1925-self-frame-pop/src/art/FramePop.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Method;
+
+public class FramePop {
+ public static native void enableFramePopEvent(Class klass, Method method, Thread thr)
+ throws Exception;
+ public static native void notifyFramePop(Thread target, int depth) throws Exception;
+}
diff --git a/test/1925-self-frame-pop/src/art/Locals.java b/test/1925-self-frame-pop/src/art/Locals.java
new file mode 100644
index 0000000..22e21be
--- /dev/null
+++ b/test/1925-self-frame-pop/src/art/Locals.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.Objects;
+
+public class Locals {
+ public static native void EnableLocalVariableAccess();
+
+ public static class VariableDescription {
+ public final long start_location;
+ public final int length;
+ public final String name;
+ public final String signature;
+ public final String generic_signature;
+ public final int slot;
+
+ public VariableDescription(
+ long start, int length, String name, String sig, String gen_sig, int slot) {
+ this.start_location = start;
+ this.length = length;
+ this.name = name;
+ this.signature = sig;
+ this.generic_signature = gen_sig;
+ this.slot = slot;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "VariableDescription { " +
+ "Sig: '%s', Name: '%s', Gen_sig: '%s', slot: %d, start: %d, len: %d" +
+ "}",
+ this.signature,
+ this.name,
+ this.generic_signature,
+ this.slot,
+ this.start_location,
+ this.length);
+ }
+ public boolean equals(Object other) {
+ if (!(other instanceof VariableDescription)) {
+ return false;
+ } else {
+ VariableDescription v = (VariableDescription)other;
+ return Objects.equals(v.signature, signature) &&
+ Objects.equals(v.name, name) &&
+ Objects.equals(v.generic_signature, generic_signature) &&
+ v.slot == slot &&
+ v.start_location == start_location &&
+ v.length == length;
+ }
+ }
+ public int hashCode() {
+ return Objects.hash(this.signature, this.name, this.generic_signature, this.slot,
+ this.start_location, this.length);
+ }
+ }
+
+ public static native VariableDescription[] GetLocalVariableTable(Executable e);
+
+ public static VariableDescription GetVariableAtLine(
+ Executable e, String name, String sig, int line) throws Exception {
+ return GetVariableAtLocation(e, name, sig, Breakpoint.lineToLocation(e, line));
+ }
+
+ public static VariableDescription GetVariableAtLocation(
+ Executable e, String name, String sig, long loc) {
+ VariableDescription[] vars = GetLocalVariableTable(e);
+ for (VariableDescription var : vars) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(name) &&
+ var.signature.equals(sig)) {
+ return var;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + name + " (sig: " + sig + ") in " + e + " at loc " + loc);
+ }
+
+ public static native int GetLocalVariableInt(Thread thr, int depth, int slot);
+ public static native long GetLocalVariableLong(Thread thr, int depth, int slot);
+ public static native float GetLocalVariableFloat(Thread thr, int depth, int slot);
+ public static native double GetLocalVariableDouble(Thread thr, int depth, int slot);
+ public static native Object GetLocalVariableObject(Thread thr, int depth, int slot);
+ public static native Object GetLocalInstance(Thread thr, int depth);
+
+ public static void SetLocalVariableInt(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableInt(thr, depth, slot, ((Number)val).intValue());
+ }
+ public static void SetLocalVariableLong(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableLong(thr, depth, slot, ((Number)val).longValue());
+ }
+ public static void SetLocalVariableFloat(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableFloat(thr, depth, slot, ((Number)val).floatValue());
+ }
+ public static void SetLocalVariableDouble(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableDouble(thr, depth, slot, ((Number)val).doubleValue());
+ }
+ public static native void SetLocalVariableInt(Thread thr, int depth, int slot, int val);
+ public static native void SetLocalVariableLong(Thread thr, int depth, int slot, long val);
+ public static native void SetLocalVariableFloat(Thread thr, int depth, int slot, float val);
+ public static native void SetLocalVariableDouble(Thread thr, int depth, int slot, double val);
+ public static native void SetLocalVariableObject(Thread thr, int depth, int slot, Object val);
+}
diff --git a/test/1925-self-frame-pop/src/art/StackTrace.java b/test/1925-self-frame-pop/src/art/StackTrace.java
new file mode 100644
index 0000000..2ea2f20
--- /dev/null
+++ b/test/1925-self-frame-pop/src/art/StackTrace.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Executable;
+
+public class StackTrace {
+ public static class StackFrameData {
+ public final Thread thr;
+ public final Executable method;
+ public final long current_location;
+ public final int depth;
+
+ public StackFrameData(Thread thr, Executable e, long loc, int depth) {
+ this.thr = thr;
+ this.method = e;
+ this.current_location = loc;
+ this.depth = depth;
+ }
+ @Override
+ public String toString() {
+ return String.format(
+ "StackFrameData { thr: '%s', method: '%s', loc: %d, depth: %d }",
+ this.thr,
+ this.method,
+ this.current_location,
+ this.depth);
+ }
+ }
+
+ public static native int GetStackDepth(Thread thr);
+
+ private static native StackFrameData[] nativeGetStackTrace(Thread thr);
+
+ public static StackFrameData[] GetStackTrace(Thread thr) {
+ // The RI seems to give inconsistent (and sometimes nonsensical) results if the thread is not
+ // suspended. The spec says that not being suspended is fine but since we want this to be
+ // consistent we will suspend for the RI.
+ boolean suspend_thread =
+ !System.getProperty("java.vm.name").equals("Dalvik") &&
+ !thr.equals(Thread.currentThread()) &&
+ !Suspension.isSuspended(thr);
+ if (suspend_thread) {
+ Suspension.suspend(thr);
+ }
+ StackFrameData[] out = nativeGetStackTrace(thr);
+ if (suspend_thread) {
+ Suspension.resume(thr);
+ }
+ return out;
+ }
+}
+
diff --git a/test/1925-self-frame-pop/src/art/Suspension.java b/test/1925-self-frame-pop/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1925-self-frame-pop/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1925-self-frame-pop/src/art/Test1925.java b/test/1925-self-frame-pop/src/art/Test1925.java
new file mode 100644
index 0000000..b413d06
--- /dev/null
+++ b/test/1925-self-frame-pop/src/art/Test1925.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.concurrent.Semaphore;
+import java.util.Arrays;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.function.IntUnaryOperator;
+import java.util.function.Function;
+
+public class Test1925 {
+ public static void handleFramePop(Executable m, boolean exception, long location) {
+ System.out.println(
+ m + " pop. Line=" + Breakpoint.locationToLine(m, location) + " exception:" + exception);
+ }
+
+ public static void recurTimesA(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesB(times - 1, safepoint);
+ }
+
+ public static void recurTimesB(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesC(times - 1, safepoint);
+ }
+
+ public static void recurTimesC(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesD(times - 1, safepoint);
+ }
+
+ public static void recurTimesD(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesE(times - 1, safepoint);
+ }
+
+ public static void recurTimesE(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesF(times - 1, safepoint);
+ }
+
+ public static void recurTimesF(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesG(times - 1, safepoint);
+ }
+
+ public static void recurTimesG(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesH(times - 1, safepoint);
+ }
+
+ public static void recurTimesH(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesI(times - 1, safepoint);
+ }
+
+ public static void recurTimesI(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesJ(times - 1, safepoint);
+ }
+
+ public static void recurTimesJ(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesK(times - 1, safepoint);
+ }
+
+ public static class RecursionError extends Error {
+ public RecursionError(String s) { super(s); }
+ }
+ public static void recurTimesK(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ safepoint.run();
+ throw new RecursionError("Unable recur further. Still " + times + " outstanding!");
+ }
+
+ public static void doRecurTestWith(final int times, int watch_frame) throws Exception {
+ final String target_method_name_start = "recurTimes";
+ final Runnable safepoint = () -> {
+ StackTrace.StackFrameData target_frame = null;
+ int cnt = 0;
+ for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(Thread.currentThread())) {
+ if (frame.method.getName().startsWith(target_method_name_start)) {
+ if (times - cnt == watch_frame) {
+ target_frame = frame;
+ break;
+ } else {
+ cnt++;
+ }
+ }
+ }
+ try {
+ FramePop.notifyFramePop(null, target_frame.depth);
+ } catch (Exception e) {
+ throw new Error("Unexpected error in notifyFramePop!", e);
+ }
+ };
+ try {
+ recurTimesA(times, safepoint);
+ System.out.println("Ran recurTimes(" + times + ") without errors!");
+ } catch (Throwable e) {
+ System.out.println("Caught exception " + e + " while running recurTimes(" + times + ")");
+ }
+ }
+
+ public static void run() throws Exception {
+ FramePop.enableFramePopEvent(
+ Test1925.class,
+ Test1925.class.getDeclaredMethod(
+ "handleFramePop", Executable.class, Boolean.TYPE, Long.TYPE),
+ null);
+ doRecurTestWith(10, 5);
+ doRecurTestWith(100, 95);
+ }
+}
diff --git a/test/1925-self-frame-pop/src/art/Trace.java b/test/1925-self-frame-pop/src/art/Trace.java
new file mode 100644
index 0000000..ba3d397
--- /dev/null
+++ b/test/1925-self-frame-pop/src/art/Trace.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class Trace {
+ public static native void enableTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Method singleStep,
+ Thread thr);
+ public static native void disableTracing(Thread thr);
+
+ public static void enableFieldTracing(Class<?> methodClass,
+ Method fieldAccess,
+ Method fieldModify,
+ Thread thr) {
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
+ }
+
+ public static void enableMethodTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Thread thr) {
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+ }
+
+ public static void enableSingleStepTracing(Class<?> methodClass,
+ Method singleStep,
+ Thread thr) {
+ enableTracing(methodClass, null, null, null, null, singleStep, thr);
+ }
+
+ public static native void watchFieldAccess(Field f);
+ public static native void watchFieldModification(Field f);
+ public static native void watchAllFieldAccesses();
+ public static native void watchAllFieldModifications();
+}
diff --git a/test/1926-missed-frame-pop/expected.txt b/test/1926-missed-frame-pop/expected.txt
new file mode 100644
index 0000000..20e723f
--- /dev/null
+++ b/test/1926-missed-frame-pop/expected.txt
@@ -0,0 +1,9 @@
+Ran recurTimes(10) without errors after disabling frame pop event!
+renabling frame pop event with similar stack.
+Ran recurTimes(10) without errors!
+Ran recurTimes(10) without errors after disabling frame pop event!
+renabling frame pop event with similar stack.
+Ran recurTimes(10) without errors!
+Ran recurTimes(10) without errors after disabling frame pop event!
+renabling frame pop event with similar stack.
+Ran recurTimes(10) without errors!
diff --git a/test/1926-missed-frame-pop/frame_pop_missed.cc b/test/1926-missed-frame-pop/frame_pop_missed.cc
new file mode 100644
index 0000000..c5104de
--- /dev/null
+++ b/test/1926-missed-frame-pop/frame_pop_missed.cc
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pthread.h>
+
+#include <cstdio>
+#include <iostream>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "jni.h"
+#include "jvmti.h"
+
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1926FramePopMissed {
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1926_disableFramePop(
+ JNIEnv* env, jclass, jthread thr) {
+ JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(
+ JVMTI_DISABLE, JVMTI_EVENT_FRAME_POP, thr));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1926_reenableFramePop(
+ JNIEnv* env, jclass, jthread thr) {
+ JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(
+ JVMTI_ENABLE, JVMTI_EVENT_FRAME_POP, thr));
+}
+
+} // namespace Test1926FramePopMissed
+} // namespace art
+
diff --git a/test/1926-missed-frame-pop/info.txt b/test/1926-missed-frame-pop/info.txt
new file mode 100644
index 0000000..b4984d9
--- /dev/null
+++ b/test/1926-missed-frame-pop/info.txt
@@ -0,0 +1,3 @@
+Tests notify frame pop JVMTI functionality.
+
+This tests the normal use case.
diff --git a/test/1926-missed-frame-pop/run b/test/1926-missed-frame-pop/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1926-missed-frame-pop/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/1926-missed-frame-pop/src/Main.java b/test/1926-missed-frame-pop/src/Main.java
new file mode 100644
index 0000000..f924079
--- /dev/null
+++ b/test/1926-missed-frame-pop/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1926.run();
+ }
+}
diff --git a/test/1926-missed-frame-pop/src/art/Breakpoint.java b/test/1926-missed-frame-pop/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1926-missed-frame-pop/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/1926-missed-frame-pop/src/art/FramePop.java b/test/1926-missed-frame-pop/src/art/FramePop.java
new file mode 100644
index 0000000..86bf226
--- /dev/null
+++ b/test/1926-missed-frame-pop/src/art/FramePop.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Method;
+
+public class FramePop {
+ public static native void enableFramePopEvent(Class klass, Method method, Thread thr)
+ throws Exception;
+ public static native void notifyFramePop(Thread target, int depth) throws Exception;
+}
diff --git a/test/1926-missed-frame-pop/src/art/Locals.java b/test/1926-missed-frame-pop/src/art/Locals.java
new file mode 100644
index 0000000..22e21be
--- /dev/null
+++ b/test/1926-missed-frame-pop/src/art/Locals.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.Objects;
+
+public class Locals {
+ public static native void EnableLocalVariableAccess();
+
+ public static class VariableDescription {
+ public final long start_location;
+ public final int length;
+ public final String name;
+ public final String signature;
+ public final String generic_signature;
+ public final int slot;
+
+ public VariableDescription(
+ long start, int length, String name, String sig, String gen_sig, int slot) {
+ this.start_location = start;
+ this.length = length;
+ this.name = name;
+ this.signature = sig;
+ this.generic_signature = gen_sig;
+ this.slot = slot;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "VariableDescription { " +
+ "Sig: '%s', Name: '%s', Gen_sig: '%s', slot: %d, start: %d, len: %d" +
+ "}",
+ this.signature,
+ this.name,
+ this.generic_signature,
+ this.slot,
+ this.start_location,
+ this.length);
+ }
+ public boolean equals(Object other) {
+ if (!(other instanceof VariableDescription)) {
+ return false;
+ } else {
+ VariableDescription v = (VariableDescription)other;
+ return Objects.equals(v.signature, signature) &&
+ Objects.equals(v.name, name) &&
+ Objects.equals(v.generic_signature, generic_signature) &&
+ v.slot == slot &&
+ v.start_location == start_location &&
+ v.length == length;
+ }
+ }
+ public int hashCode() {
+ return Objects.hash(this.signature, this.name, this.generic_signature, this.slot,
+ this.start_location, this.length);
+ }
+ }
+
+ public static native VariableDescription[] GetLocalVariableTable(Executable e);
+
+ public static VariableDescription GetVariableAtLine(
+ Executable e, String name, String sig, int line) throws Exception {
+ return GetVariableAtLocation(e, name, sig, Breakpoint.lineToLocation(e, line));
+ }
+
+ public static VariableDescription GetVariableAtLocation(
+ Executable e, String name, String sig, long loc) {
+ VariableDescription[] vars = GetLocalVariableTable(e);
+ for (VariableDescription var : vars) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(name) &&
+ var.signature.equals(sig)) {
+ return var;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + name + " (sig: " + sig + ") in " + e + " at loc " + loc);
+ }
+
+ public static native int GetLocalVariableInt(Thread thr, int depth, int slot);
+ public static native long GetLocalVariableLong(Thread thr, int depth, int slot);
+ public static native float GetLocalVariableFloat(Thread thr, int depth, int slot);
+ public static native double GetLocalVariableDouble(Thread thr, int depth, int slot);
+ public static native Object GetLocalVariableObject(Thread thr, int depth, int slot);
+ public static native Object GetLocalInstance(Thread thr, int depth);
+
+ public static void SetLocalVariableInt(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableInt(thr, depth, slot, ((Number)val).intValue());
+ }
+ public static void SetLocalVariableLong(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableLong(thr, depth, slot, ((Number)val).longValue());
+ }
+ public static void SetLocalVariableFloat(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableFloat(thr, depth, slot, ((Number)val).floatValue());
+ }
+ public static void SetLocalVariableDouble(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableDouble(thr, depth, slot, ((Number)val).doubleValue());
+ }
+ public static native void SetLocalVariableInt(Thread thr, int depth, int slot, int val);
+ public static native void SetLocalVariableLong(Thread thr, int depth, int slot, long val);
+ public static native void SetLocalVariableFloat(Thread thr, int depth, int slot, float val);
+ public static native void SetLocalVariableDouble(Thread thr, int depth, int slot, double val);
+ public static native void SetLocalVariableObject(Thread thr, int depth, int slot, Object val);
+}
diff --git a/test/1926-missed-frame-pop/src/art/StackTrace.java b/test/1926-missed-frame-pop/src/art/StackTrace.java
new file mode 100644
index 0000000..2ea2f20
--- /dev/null
+++ b/test/1926-missed-frame-pop/src/art/StackTrace.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Executable;
+
+public class StackTrace {
+ public static class StackFrameData {
+ public final Thread thr;
+ public final Executable method;
+ public final long current_location;
+ public final int depth;
+
+ public StackFrameData(Thread thr, Executable e, long loc, int depth) {
+ this.thr = thr;
+ this.method = e;
+ this.current_location = loc;
+ this.depth = depth;
+ }
+ @Override
+ public String toString() {
+ return String.format(
+ "StackFrameData { thr: '%s', method: '%s', loc: %d, depth: %d }",
+ this.thr,
+ this.method,
+ this.current_location,
+ this.depth);
+ }
+ }
+
+ public static native int GetStackDepth(Thread thr);
+
+ private static native StackFrameData[] nativeGetStackTrace(Thread thr);
+
+ public static StackFrameData[] GetStackTrace(Thread thr) {
+ // The RI seems to give inconsistent (and sometimes nonsensical) results if the thread is not
+ // suspended. The spec says that not being suspended is fine but since we want this to be
+ // consistent we will suspend for the RI.
+ boolean suspend_thread =
+ !System.getProperty("java.vm.name").equals("Dalvik") &&
+ !thr.equals(Thread.currentThread()) &&
+ !Suspension.isSuspended(thr);
+ if (suspend_thread) {
+ Suspension.suspend(thr);
+ }
+ StackFrameData[] out = nativeGetStackTrace(thr);
+ if (suspend_thread) {
+ Suspension.resume(thr);
+ }
+ return out;
+ }
+}
+
diff --git a/test/1926-missed-frame-pop/src/art/Suspension.java b/test/1926-missed-frame-pop/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1926-missed-frame-pop/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1926-missed-frame-pop/src/art/Test1926.java b/test/1926-missed-frame-pop/src/art/Test1926.java
new file mode 100644
index 0000000..cb21072
--- /dev/null
+++ b/test/1926-missed-frame-pop/src/art/Test1926.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.concurrent.Semaphore;
+import java.util.Arrays;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.function.IntUnaryOperator;
+import java.util.function.Function;
+
+public class Test1926 {
+ public static void handleFramePop(Executable m, boolean exception, long location) {
+ System.out.println(
+ m + " pop. Line=" + Breakpoint.locationToLine(m, location) + " exception:" + exception);
+ }
+
+ public static void recurTimesA(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesB(times - 1, safepoint);
+ }
+
+ public static void recurTimesB(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesC(times - 1, safepoint);
+ }
+
+ public static void recurTimesC(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesD(times - 1, safepoint);
+ }
+
+ public static void recurTimesD(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesE(times - 1, safepoint);
+ }
+
+ public static void recurTimesE(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesF(times - 1, safepoint);
+ }
+
+ public static void recurTimesF(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesG(times - 1, safepoint);
+ }
+
+ public static void recurTimesG(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesH(times - 1, safepoint);
+ }
+
+ public static void recurTimesH(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesI(times - 1, safepoint);
+ }
+
+ public static void recurTimesI(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesJ(times - 1, safepoint);
+ }
+
+ public static void recurTimesJ(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesK(times - 1, safepoint);
+ }
+
+ public static void recurTimesK(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ recurTimesL(times - 1, safepoint);
+ }
+
+ public static class RecursionError extends Error {
+ public RecursionError(String s) { super(s); }
+ }
+
+ public static void recurTimesL(int times, Runnable safepoint) {
+ if (times == 0) {
+ safepoint.run();
+ return;
+ }
+ safepoint.run();
+ throw new RecursionError("Unable recur further. Still " + times + " outstanding!");
+ }
+
+ public static class ThreadPauser implements Runnable {
+ public final Semaphore sem_wakeup_main;
+ public final Semaphore sem_wait;
+
+ public ThreadPauser() {
+ sem_wakeup_main = new Semaphore(0);
+ sem_wait = new Semaphore(0);
+ }
+
+ public void run() {
+ try {
+ sem_wakeup_main.release();
+ sem_wait.acquire();
+ } catch (Exception e) {
+ throw new Error("Error with semaphores!", e);
+ }
+ }
+
+ public void waitForOtherThreadToPause() throws Exception {
+ sem_wakeup_main.acquire();
+ }
+
+ public void wakeupOtherThread() throws Exception {
+ sem_wait.release();
+ }
+ }
+
+ public static void doRecurTestWith(final int times, int watch_frame) throws Exception {
+ final String target_method_name_start = "recurTimes";
+ final ThreadPauser safepoint = new ThreadPauser();
+ Thread target = new Thread(() -> {
+ recurTimesA(times, () -> {
+ safepoint.run();
+ disableFramePop(null);
+ });
+ System.out.println("Ran recurTimes(" + times + ") without errors after disabling " +
+ "frame pop event!");
+ System.out.println("renabling frame pop event with similar stack.");
+ recurTimesB(times, () -> { reenableFramePop(null); });
+ System.out.println("Ran recurTimes(" + times + ") without errors!");
+ });
+ target.start();
+ safepoint.waitForOtherThreadToPause();
+ Suspension.suspend(target);
+ // Safe block
+ int cnt = 0;
+ StackTrace.StackFrameData target_frame = null;
+ for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(target)) {
+ if (frame.method.getName().startsWith(target_method_name_start)) {
+ if (times - cnt == watch_frame) {
+ target_frame = frame;
+ break;
+ } else {
+ cnt++;
+ }
+ }
+ }
+ if (target_frame != null) {
+ FramePop.notifyFramePop(target, target_frame.depth);
+ } else {
+ System.out.println(
+ "Unable to find stack frame for " + watch_frame + " depth of "
+ + target_method_name_start);
+ }
+ Suspension.resume(target);
+ safepoint.wakeupOtherThread();
+ target.join();
+ }
+
+ public static void run() throws Exception {
+ // Listen for events on all threads.
+ FramePop.enableFramePopEvent(
+ Test1926.class,
+ Test1926.class.getDeclaredMethod(
+ "handleFramePop", Executable.class, Boolean.TYPE, Long.TYPE),
+ null);
+ doRecurTestWith(10, 0);
+ doRecurTestWith(10, 5);
+ doRecurTestWith(10, 10);
+ }
+
+ public static native void disableFramePop(Thread thr);
+ public static native void reenableFramePop(Thread thr);
+}
diff --git a/test/1926-missed-frame-pop/src/art/Trace.java b/test/1926-missed-frame-pop/src/art/Trace.java
new file mode 100644
index 0000000..ba3d397
--- /dev/null
+++ b/test/1926-missed-frame-pop/src/art/Trace.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class Trace {
+ public static native void enableTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Method singleStep,
+ Thread thr);
+ public static native void disableTracing(Thread thr);
+
+ public static void enableFieldTracing(Class<?> methodClass,
+ Method fieldAccess,
+ Method fieldModify,
+ Thread thr) {
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
+ }
+
+ public static void enableMethodTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Thread thr) {
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+ }
+
+ public static void enableSingleStepTracing(Class<?> methodClass,
+ Method singleStep,
+ Thread thr) {
+ enableTracing(methodClass, null, null, null, null, singleStep, thr);
+ }
+
+ public static native void watchFieldAccess(Field f);
+ public static native void watchFieldModification(Field f);
+ public static native void watchAllFieldAccesses();
+ public static native void watchAllFieldModifications();
+}
diff --git a/test/661-checker-simd-reduc/src/Main.java b/test/661-checker-simd-reduc/src/Main.java
index 741b5fa..8208a9e 100644
--- a/test/661-checker-simd-reduc/src/Main.java
+++ b/test/661-checker-simd-reduc/src/Main.java
@@ -51,6 +51,26 @@
return sum;
}
+ /// CHECK-START: int Main.reductionInt(int[]) loop_optimization (before)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Cons1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get:i\d+>> ArrayGet [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi2>>,<<Get>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: int Main.reductionInt(int[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none
+ /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecAdd [<<Phi2>>,<<Load>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons4>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi2>>] loop:none
+ /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none
private static int reductionInt(int[] x) {
int sum = 0;
for (int i = 0; i < x.length; i++) {
@@ -59,6 +79,28 @@
return sum;
}
+ /// CHECK-START: long Main.reductionLong(long[]) loop_optimization (before)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Long0:j\d+>> LongConstant 0 loop:none
+ /// CHECK-DAG: <<Cons1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:j\d+>> Phi [<<Long0>>,{{j\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get:j\d+>> ArrayGet [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi2>>,<<Get>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: long Main.reductionLong(long[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Long0:j\d+>> LongConstant 0 loop:none
+ /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none
+ /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Long0>>] loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecAdd [<<Phi2>>,<<Load>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi2>>] loop:none
+ /// CHECK-DAG: <<Extr:j\d+>> VecExtractScalar [<<Red>>] loop:none
private static long reductionLong(long[] x) {
long sum = 0;
for (int i = 0; i < x.length; i++) {
@@ -67,6 +109,90 @@
return sum;
}
+ private static byte reductionByteM1(byte[] x) {
+ byte sum = -1;
+ for (int i = 0; i < x.length; i++) {
+ sum += x[i];
+ }
+ return sum;
+ }
+
+ private static short reductionShortM1(short[] x) {
+ short sum = -1;
+ for (int i = 0; i < x.length; i++) {
+ sum += x[i];
+ }
+ return sum;
+ }
+
+ private static char reductionCharM1(char[] x) {
+ char sum = 0xffff;
+ for (int i = 0; i < x.length; i++) {
+ sum += x[i];
+ }
+ return sum;
+ }
+
+ /// CHECK-START: int Main.reductionIntM1(int[]) loop_optimization (before)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Cons1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<ConsM1:i\d+>> IntConstant -1 loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi [<<ConsM1>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get:i\d+>> ArrayGet [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi2>>,<<Get>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: int Main.reductionIntM1(int[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<ConsM1:i\d+>> IntConstant -1 loop:none
+ /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none
+ /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<ConsM1>>] loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecAdd [<<Phi2>>,<<Load>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons4>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi2>>] loop:none
+ /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none
+ private static int reductionIntM1(int[] x) {
+ int sum = -1;
+ for (int i = 0; i < x.length; i++) {
+ sum += x[i];
+ }
+ return sum;
+ }
+
+ /// CHECK-START: long Main.reductionLongM1(long[]) loop_optimization (before)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<LongM1:j\d+>> LongConstant -1 loop:none
+ /// CHECK-DAG: <<Cons1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:j\d+>> Phi [<<LongM1>>,{{j\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get:j\d+>> ArrayGet [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi2>>,<<Get>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: long Main.reductionLongM1(long[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<LongM1:j\d+>> LongConstant -1 loop:none
+ /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none
+ /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<LongM1>>] loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecAdd [<<Phi2>>,<<Load>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi2>>] loop:none
+ /// CHECK-DAG: <<Extr:j\d+>> VecExtractScalar [<<Red>>] loop:none
+ private static long reductionLongM1(long[] x) {
+ long sum = -1L;
+ for (int i = 0; i < x.length; i++) {
+ sum += x[i];
+ }
+ return sum;
+ }
+
private static byte reductionMinusByte(byte[] x) {
byte sum = 0;
for (int i = 0; i < x.length; i++) {
@@ -91,6 +217,26 @@
return sum;
}
+ /// CHECK-START: int Main.reductionMinusInt(int[]) loop_optimization (before)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Cons1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get:i\d+>> ArrayGet [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Sub [<<Phi2>>,<<Get>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: int Main.reductionMinusInt(int[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none
+ /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecSub [<<Phi2>>,<<Load>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons4>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi2>>] loop:none
+ /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none
private static int reductionMinusInt(int[] x) {
int sum = 0;
for (int i = 0; i < x.length; i++) {
@@ -99,6 +245,28 @@
return sum;
}
+ /// CHECK-START: long Main.reductionMinusLong(long[]) loop_optimization (before)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Long0:j\d+>> LongConstant 0 loop:none
+ /// CHECK-DAG: <<Cons1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:j\d+>> Phi [<<Long0>>,{{j\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get:j\d+>> ArrayGet [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Sub [<<Phi2>>,<<Get>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: long Main.reductionMinusLong(long[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Long0:j\d+>> LongConstant 0 loop:none
+ /// CHECK-DAG: <<Cons2:i\d+>> IntConstant 2 loop:none
+ /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Long0>>] loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecSub [<<Phi2>>,<<Load>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi2>>] loop:none
+ /// CHECK-DAG: <<Extr:j\d+>> VecExtractScalar [<<Red>>] loop:none
private static long reductionMinusLong(long[] x) {
long sum = 0;
for (int i = 0; i < x.length; i++) {
@@ -131,6 +299,28 @@
return min;
}
+ /// CHECK-START: int Main.reductionMinInt(int[]) loop_optimization (before)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Cons1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<ConsM:i\d+>> IntConstant 2147483647 loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi [<<ConsM>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get:i\d+>> ArrayGet [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Phi2>>,<<Get>>] intrinsic:MathMinIntInt loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: int Main.reductionMinInt(int[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<ConsM:i\d+>> IntConstant 2147483647 loop:none
+ /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none
+ /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<ConsM>>] loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecMin [<<Phi2>>,<<Load>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons4>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi2>>] loop:none
+ /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none
private static int reductionMinInt(int[] x) {
int min = Integer.MAX_VALUE;
for (int i = 0; i < x.length; i++) {
@@ -171,6 +361,28 @@
return max;
}
+ /// CHECK-START: int Main.reductionMaxInt(int[]) loop_optimization (before)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Cons1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<ConsM:i\d+>> IntConstant -2147483648 loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi [<<ConsM>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get:i\d+>> ArrayGet [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Phi2>>,<<Get>>] intrinsic:MathMaxIntInt loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: int Main.reductionMaxInt(int[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<ConsM:i\d+>> IntConstant -2147483648 loop:none
+ /// CHECK-DAG: <<Cons4:i\d+>> IntConstant 4 loop:none
+ /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<ConsM>>] loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecMax [<<Phi2>>,<<Load>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons4>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Red:d\d+>> VecReduce [<<Phi2>>] loop:none
+ /// CHECK-DAG: <<Extr:i\d+>> VecExtractScalar [<<Red>>] loop:none
private static int reductionMaxInt(int[] x) {
int max = Integer.MIN_VALUE;
for (int i = 0; i < x.length; i++) {
@@ -253,6 +465,11 @@
expectEquals(38070, reductionChar(xc));
expectEquals(365750, reductionInt(xi));
expectEquals(365750L, reductionLong(xl));
+ expectEquals(-75, reductionByteM1(xb));
+ expectEquals(-27467, reductionShortM1(xs));
+ expectEquals(38069, reductionCharM1(xc));
+ expectEquals(365749, reductionIntM1(xi));
+ expectEquals(365749L, reductionLongM1(xl));
expectEquals(74, reductionMinusByte(xb));
expectEquals(27466, reductionMinusShort(xs));
expectEquals(27466, reductionMinusChar(xc));
diff --git a/test/Android.bp b/test/Android.bp
index 4d82cbb..2aed50c 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -248,6 +248,7 @@
"ti-agent/test_env.cc",
"ti-agent/breakpoint_helper.cc",
"ti-agent/common_helper.cc",
+ "ti-agent/frame_pop_helper.cc",
"ti-agent/locals_helper.cc",
"ti-agent/redefinition_helper.cc",
"ti-agent/suspension_helper.cc",
@@ -294,6 +295,9 @@
"1919-vminit-thread-start-timing/vminit.cc",
"1920-suspend-native-monitor/native_suspend_monitor.cc",
"1921-suspend-native-recursive-monitor/native_suspend_recursive_monitor.cc",
+ "1922-owned-monitors-info/owned_monitors.cc",
+ "1924-frame-pop-toggle/frame_pop_toggle.cc",
+ "1926-missed-frame-pop/frame_pop_missed.cc",
],
shared_libs: [
"libbase",
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 20cfc34..df7544e 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -681,6 +681,16 @@
"env_vars": {"SANITIZE_HOST": "address"}
},
{
+ "tests": [
+ "1923-frame-pop",
+ "1924-frame-pop-toggle"
+ ],
+ "description": "ASAN seems to make these tests overflow their stacks.",
+ "variant": "64",
+ "bug": "b/65189092",
+ "env_vars": {"SANITIZE_HOST": "address"}
+ },
+ {
"tests": ["988-method-trace"],
"variant": "redefine-stress | jvmti-stress",
"description": "Test disabled due to redefine-stress disabling intrinsics which changes the trace output slightly."
diff --git a/test/run-test b/test/run-test
index e6196a0..9996986 100755
--- a/test/run-test
+++ b/test/run-test
@@ -407,6 +407,10 @@
elif [ "x$1" = "x--random-profile" ]; then
run_args="${run_args} --random-profile"
shift
+ elif [ "x$1" = "x--dex2oat-jobs" ]; then
+ shift
+ run_args="${run_args} -Xcompiler-option -j$1"
+ shift
elif expr "x$1" : "x--" >/dev/null 2>&1; then
echo "unknown $0 option: $1" 1>&2
usage="yes"
@@ -702,6 +706,7 @@
echo " --bisection-search Perform bisection bug search."
echo " --vdex Test using vdex as in input to dex2oat. Only works with --prebuild."
echo " --suspend-timeout Change thread suspend timeout ms (default 500000)."
+ echo " --dex2oat-jobs Number of dex2oat jobs."
) 1>&2 # Direct to stderr so usage is not printed if --quiet is set.
exit 1
fi
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 68e1856..f425097 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -127,6 +127,7 @@
gdb = False
gdb_arg = ''
stop_testrunner = False
+dex2oat_jobs = -1 # -1 corresponds to default threads for dex2oat
def gather_test_info():
"""The method gathers test information about the test to be run which includes
@@ -341,6 +342,9 @@
if gdb_arg:
options_all += ' --gdb-arg ' + gdb_arg
+ if dex2oat_jobs != -1:
+ options_all += ' --dex2oat-jobs ' + str(dex2oat_jobs)
+
config = itertools.product(tests, TARGET_TYPES, RUN_TYPES, PREBUILD_TYPES,
COMPILER_TYPES, RELOCATE_TYPES, TRACE_TYPES,
GC_TYPES, JNI_TYPES, IMAGE_TYPES, PICTEST_TYPES,
@@ -860,6 +864,7 @@
global gdb
global gdb_arg
global timeout
+ global dex2oat_jobs
parser = argparse.ArgumentParser(description="Runs all or a subset of the ART test suite.")
parser.add_argument('-t', '--test', dest='test', help='name of the test')
@@ -887,6 +892,8 @@
parser.set_defaults(build = env.ART_TEST_RUN_TEST_BUILD)
parser.add_argument('--gdb', action='store_true', dest='gdb')
parser.add_argument('--gdb-arg', dest='gdb_arg')
+ parser.add_argument('--dex2oat-jobs', type=int, dest='dex2oat_jobs',
+ help='Number of dex2oat jobs')
options = vars(parser.parse_args())
if options['build_target']:
@@ -987,6 +994,8 @@
if options['gdb_arg']:
gdb_arg = options['gdb_arg']
timeout = options['timeout']
+ if options['dex2oat_jobs']:
+ dex2oat_jobs = options['dex2oat_jobs']
return test
diff --git a/test/ti-agent/frame_pop_helper.cc b/test/ti-agent/frame_pop_helper.cc
new file mode 100644
index 0000000..4571032
--- /dev/null
+++ b/test/ti-agent/frame_pop_helper.cc
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common_helper.h"
+
+#include "jni.h"
+#include "jvmti.h"
+
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "test_env.h"
+
+namespace art {
+namespace common_frame_pop {
+
+struct FramePopData {
+ jclass test_klass;
+ jmethodID pop_method;
+};
+
+static void framePopCB(jvmtiEnv* jvmti,
+ JNIEnv* jnienv,
+ jthread thr,
+ jmethodID method ATTRIBUTE_UNUSED,
+ jboolean was_popped_by_exception) {
+ FramePopData* data = nullptr;
+ if (JvmtiErrorToException(jnienv, jvmti,
+ jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ jlong location;
+ jmethodID frame_method;
+ if (JvmtiErrorToException(jnienv,
+ jvmti,
+ jvmti->GetFrameLocation(thr, 0, &frame_method, &location))) {
+ return;
+ }
+ CHECK(data->pop_method != nullptr);
+ jobject method_arg = GetJavaMethod(jvmti, jnienv, frame_method);
+ jnienv->CallStaticVoidMethod(data->test_klass,
+ data->pop_method,
+ method_arg,
+ was_popped_by_exception,
+ location);
+ jnienv->DeleteLocalRef(method_arg);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_FramePop_enableFramePopEvent(
+ JNIEnv* env, jclass, jclass klass, jobject notify_method, jthread thr) {
+ FramePopData* data = nullptr;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->Allocate(sizeof(FramePopData),
+ reinterpret_cast<unsigned char**>(&data)))) {
+ return;
+ }
+ memset(data, 0, sizeof(FramePopData));
+ data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(klass));
+ data->pop_method = env->FromReflectedMethod(notify_method);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ void* old_data = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) {
+ return;
+ } else if (old_data != nullptr) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
+ return;
+ }
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) {
+ return;
+ }
+ jvmtiCapabilities caps;
+ memset(&caps, 0, sizeof(caps));
+ caps.can_generate_frame_pop_events = 1;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps))) {
+ return;
+ }
+ jvmtiEventCallbacks cb;
+ memset(&cb, 0, sizeof(cb));
+ cb.FramePop = framePopCB;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
+ return;
+ }
+ JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_FRAME_POP,
+ thr));
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_art_FramePop_makeJvmtiEnvForFramePop(JNIEnv* env, jclass) {
+ JavaVM* vm;
+ jvmtiEnv* out_jvmti_env = nullptr;
+ if (env->GetJavaVM(&vm) != JNI_OK ||
+ vm->GetEnv(reinterpret_cast<void**>(&out_jvmti_env), JVMTI_VERSION_1_0) != JNI_OK) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ if (rt_exception.get() == nullptr) {
+ // CNFE should be pending.
+ return 0L;
+ }
+ env->ThrowNew(rt_exception.get(), "Unable to create new jvmti_env");
+ return 0L;
+ }
+ SetAllCapabilities(out_jvmti_env);
+ return static_cast<jlong>(reinterpret_cast<intptr_t>(out_jvmti_env));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_FramePop_notifyFramePop(
+ JNIEnv* env, jclass, jthread thr, jint depth) {
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->NotifyFramePop(thr, depth));
+}
+
+} // namespace common_frame_pop
+} // namespace art
+
diff --git a/tools/cpp-define-generator/constant_globals.def b/tools/cpp-define-generator/constant_globals.def
index dbaf33c..5018f52 100644
--- a/tools/cpp-define-generator/constant_globals.def
+++ b/tools/cpp-define-generator/constant_globals.def
@@ -19,6 +19,7 @@
#if defined(DEFINE_INCLUDE_DEPENDENCIES)
#include <atomic> // std::memory_order_relaxed
#include "globals.h" // art::kObjectAlignment
+#include "modifiers.h"
#endif
DEFINE_EXPR(STD_MEMORY_ORDER_RELAXED, int32_t, std::memory_order_relaxed)
@@ -30,5 +31,8 @@
DEFINE_OBJECT_EXPR(ALIGNMENT_MASK_TOGGLED, uint32_t, ~static_cast<uint32_t>(art::kObjectAlignment - 1))
DEFINE_OBJECT_EXPR(ALIGNMENT_MASK_TOGGLED64, uint64_t, ~static_cast<uint64_t>(art::kObjectAlignment - 1))
+DEFINE_EXPR(ACC_OBSOLETE_METHOD, int32_t, art::kAccObsoleteMethod)
+DEFINE_EXPR(ACC_OBSOLETE_METHOD_SHIFT, int32_t, art::WhichPowerOf2(art::kAccObsoleteMethod))
+
#undef DEFINE_OBJECT_EXPR
diff --git a/tools/cpp-define-generator/offset_dexcache.def b/tools/cpp-define-generator/offset_art_method.def
similarity index 95%
rename from tools/cpp-define-generator/offset_dexcache.def
rename to tools/cpp-define-generator/offset_art_method.def
index 43f9434..e6a0907 100644
--- a/tools/cpp-define-generator/offset_dexcache.def
+++ b/tools/cpp-define-generator/offset_art_method.def
@@ -33,10 +33,10 @@
DEFINE_EXPR(DECLARING_CLASS_ ## field_name ## _OFFSET, int32_t, art::mirror::Class::method_name##Offset().Int32Value())
// New macro suffix Method Name (of the Offset method)
-DEFINE_ART_METHOD_OFFSET_SIZED(DEX_CACHE_METHODS, DexCacheResolvedMethods)
DEFINE_ART_METHOD_OFFSET_SIZED(JNI, EntryPointFromJni)
DEFINE_ART_METHOD_OFFSET_SIZED(QUICK_CODE, EntryPointFromQuickCompiledCode)
DEFINE_ART_METHOD_OFFSET(DECLARING_CLASS, DeclaringClass)
+DEFINE_ART_METHOD_OFFSET(ACCESS_FLAGS, AccessFlags)
#undef DEFINE_ART_METHOD_OFFSET
#undef DEFINE_ART_METHOD_OFFSET_32
diff --git a/tools/cpp-define-generator/offset_mirror_class.def b/tools/cpp-define-generator/offset_mirror_class.def
new file mode 100644
index 0000000..9b7bfce
--- /dev/null
+++ b/tools/cpp-define-generator/offset_mirror_class.def
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Offsets within java.lang.Class (mirror::Class).
+
+#if defined(DEFINE_INCLUDE_DEPENDENCIES)
+#include "mirror/class.h" // art::mirror::Object
+#endif
+
+#include "common.def" // DEFINE_OFFSET_EXPR
+
+#define DEFINE_MIRROR_CLASS_OFFSET(field_name, method_name) \
+ DEFINE_OFFSET_EXPR(MIRROR_CLASS, field_name, int32_t, art::mirror::Class::method_name##Offset().Int32Value())
+
+// New macro suffix Method Name (of the Offset method)
+DEFINE_MIRROR_CLASS_OFFSET(DEX_CACHE, DexCache)
+
+#undef DEFINE_MIRROR_CLASS_OFFSET
+#include "common_undef.def" // undef DEFINE_OFFSET_EXPR
diff --git a/tools/cpp-define-generator/offset_mirror_dex_cache.def b/tools/cpp-define-generator/offset_mirror_dex_cache.def
new file mode 100644
index 0000000..8f008bb
--- /dev/null
+++ b/tools/cpp-define-generator/offset_mirror_dex_cache.def
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Offsets within java.lang.DexCache (mirror::DexCache).
+
+#if defined(DEFINE_INCLUDE_DEPENDENCIES)
+#include "mirror/class.h" // art::mirror::Object
+#endif
+
+#include "common.def" // DEFINE_OFFSET_EXPR
+
+#define DEFINE_MIRROR_DEX_CACHE_OFFSET(field_name, method_name) \
+ DEFINE_OFFSET_EXPR(MIRROR_DEX_CACHE, field_name, int32_t, art::mirror::DexCache::method_name##Offset().Int32Value())
+
+// New macro suffix Method Name (of the Offset method)
+DEFINE_MIRROR_DEX_CACHE_OFFSET(RESOLVED_METHODS, ResolvedMethods)
+
+#undef DEFINE_MIRROR_CLASS_OFFSET
+#include "common_undef.def" // undef DEFINE_OFFSET_EXPR
diff --git a/tools/cpp-define-generator/offsets_all.def b/tools/cpp-define-generator/offsets_all.def
index b8947de..c2e8c97 100644
--- a/tools/cpp-define-generator/offsets_all.def
+++ b/tools/cpp-define-generator/offsets_all.def
@@ -42,12 +42,13 @@
// #include "offset_shadow_frame.def"
#include "offset_codeitem.def"
// TODO: MIRROR_OBJECT_HEADER_SIZE (depends on #ifdef read barrier)
-// TODO: MIRROR_CLASS offsets (see above)
+#include "offset_mirror_class.def"
+#include "offset_mirror_dex_cache.def"
#include "offset_mirror_object.def"
#include "constant_class.def"
// TODO: MIRROR_*_ARRAY offsets (depends on header size)
// TODO: MIRROR_STRING offsets (depends on header size)
-#include "offset_dexcache.def"
+#include "offset_art_method.def"
#include "constant_dexcache.def"
#include "constant_card_table.def"
#include "constant_heap.def"
diff --git a/tools/jfuzz/README.md b/tools/jfuzz/README.md
index c87e714..10d175b 100644
--- a/tools/jfuzz/README.md
+++ b/tools/jfuzz/README.md
@@ -48,6 +48,7 @@
[--report_script=SCRIPT]
[--jfuzz_arg=ARG]
[--true_divergence]
+ [--use_dx]
where
@@ -63,6 +64,7 @@
--report_script : path to script called for each divergence
--jfuzz_arg : argument for jfuzz
--true_divergence : don't bisect timeout divergences
+ --use_dx : use dx (rather than jack)
How to start JFuzz nightly testing
==================================
@@ -83,12 +85,14 @@
[--num_tests=NUM_TESTS]
[--num_inputs=NUM_INPUTS]
[--device=DEVICE]
+ [--use_dx]
where
--num_tests : number of tests to run (10000 by default)
--num_inputs: number of JFuzz programs to generate
--device : target device serial number (passed to adb -s)
+ --use_dx : use dx (rather than jack)
Background
==========
diff --git a/tools/jfuzz/run_dex_fuzz_test.py b/tools/jfuzz/run_dex_fuzz_test.py
index c1d2e4f..ca0aec0 100755
--- a/tools/jfuzz/run_dex_fuzz_test.py
+++ b/tools/jfuzz/run_dex_fuzz_test.py
@@ -19,6 +19,7 @@
import shutil
import sys
+from glob import glob
from subprocess import call
from tempfile import mkdtemp
@@ -40,13 +41,14 @@
class DexFuzzTester(object):
"""Tester that feeds JFuzz programs into DexFuzz testing."""
- def __init__(self, num_tests, num_inputs, device):
+ def __init__(self, num_tests, num_inputs, device, use_dx):
"""Constructor for the tester.
Args:
num_tests: int, number of tests to run
num_inputs: int, number of JFuzz programs to generate
device: string, target device serial number (or None)
+ use_dx: boolean, if True use dx rather than jack
"""
self._num_tests = num_tests
self._num_inputs = num_inputs
@@ -56,6 +58,7 @@
self._dexfuzz_dir = None
self._inputs_dir = None
self._dexfuzz_env = None
+ self._use_dx = use_dx
def __enter__(self):
"""On entry, enters new temp directory after saving current directory.
@@ -100,6 +103,35 @@
self.GenerateJFuzzPrograms()
self.RunDexFuzz()
+ def CompileOnHost(self):
+ """Compiles Test.java into classes.dex using either javac/dx or jack.
+
+ Raises:
+ FatalError: error when compilation fails
+ """
+ if self._use_dx:
+ if RunCommand(['javac', 'Test.java'],
+ out=None, err='jerr.txt', timeout=30) != RetCode.SUCCESS:
+ print('Unexpected error while running javac')
+ raise FatalError('Unexpected error while running javac')
+ cfiles = glob('*.class')
+ if RunCommand(['dx', '--dex', '--output=classes.dex'] + cfiles,
+ out=None, err='dxerr.txt', timeout=30) != RetCode.SUCCESS:
+ print('Unexpected error while running dx')
+ raise FatalError('Unexpected error while running dx')
+ # Cleanup on success (nothing to see).
+ for cfile in cfiles:
+ os.unlink(cfile)
+ os.unlink('jerr.txt')
+ os.unlink('dxerr.txt')
+ else:
+ jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', 'Test.java']
+ if RunCommand(['jack'] + jack_args, out=None, err='jackerr.txt',
+ timeout=30) != RetCode.SUCCESS:
+ print('Unexpected error while running Jack')
+ raise FatalError('Unexpected error while running Jack')
+ # Cleanup on success (nothing to see).
+ os.unlink('jackerr.txt')
def GenerateJFuzzPrograms(self):
"""Generates JFuzz programs.
@@ -109,17 +141,12 @@
"""
os.chdir(self._inputs_dir)
for i in range(1, self._num_inputs + 1):
- jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', 'Test.java']
if RunCommand(['jfuzz'], out='Test.java', err=None) != RetCode.SUCCESS:
print('Unexpected error while running JFuzz')
raise FatalError('Unexpected error while running JFuzz')
- if RunCommand(['jack'] + jack_args, out=None, err='jackerr.txt',
- timeout=30) != RetCode.SUCCESS:
- print('Unexpected error while running Jack')
- raise FatalError('Unexpected error while running Jack')
+ self.CompileOnHost()
shutil.move('Test.java', '../Test' + str(i) + '.java')
shutil.move('classes.dex', 'classes' + str(i) + '.dex')
- os.unlink('jackerr.txt')
def RunDexFuzz(self):
"""Starts the DexFuzz testing."""
@@ -152,10 +179,12 @@
type=int, help='number of tests to run')
parser.add_argument('--num_inputs', default=10,
type=int, help='number of JFuzz program to generate')
+ parser.add_argument('--use_dx', default=False, action='store_true',
+ help='use dx (rather than jack)')
parser.add_argument('--device', help='target device serial number')
args = parser.parse_args()
# Run the DexFuzz tester.
- with DexFuzzTester(args.num_tests, args.num_inputs, args.device) as fuzzer:
+ with DexFuzzTester(args.num_tests, args.num_inputs, args.device, args.use_dx) as fuzzer:
fuzzer.Run()
if __name__ == '__main__':
diff --git a/tools/jfuzz/run_jfuzz_test.py b/tools/jfuzz/run_jfuzz_test.py
index 58bc737..dac1c79 100755
--- a/tools/jfuzz/run_jfuzz_test.py
+++ b/tools/jfuzz/run_jfuzz_test.py
@@ -606,7 +606,7 @@
parser.add_argument('--true_divergence', default=False, action='store_true',
help='don\'t bisect timeout divergences')
parser.add_argument('--use_dx', default=False, action='store_true',
- help='use old-style dx (rather than jack)')
+ help='use dx (rather than jack)')
args = parser.parse_args()
if args.mode1 == args.mode2:
raise FatalError('Identical execution modes given')