diff options
108 files changed, 6276 insertions, 819 deletions
diff --git a/compiler/dex/quick_compiler_callbacks.cc b/compiler/dex/quick_compiler_callbacks.cc index c7e9f4fc07..23511e55fc 100644 --- a/compiler/dex/quick_compiler_callbacks.cc +++ b/compiler/dex/quick_compiler_callbacks.cc @@ -34,17 +34,21 @@ void QuickCompilerCallbacks::ClassRejected(ClassReference ref) { } } -bool QuickCompilerCallbacks::CanAssumeVerified(ClassReference ref) { +ClassStatus QuickCompilerCallbacks::GetPreviousClassState(ClassReference ref) { // If we don't have class unloading enabled in the compiler, we will never see class that were // previously verified. Return false to avoid overhead from the lookup in the compiler driver. if (!does_class_unloading_) { - return false; + return ClassStatus::kStatusNotReady; } DCHECK(compiler_driver_ != nullptr); // In the case of the quicken filter: avoiding verification of quickened instructions, which the // verifier doesn't currently support. // In the case of the verify filter, avoiding verifiying twice. - return compiler_driver_->CanAssumeVerified(ref); + ClassStatus status; + if (!compiler_driver_->GetCompiledClass(ref, &status)) { + return ClassStatus::kStatusNotReady; + } + return status; } } // namespace art diff --git a/compiler/dex/quick_compiler_callbacks.h b/compiler/dex/quick_compiler_callbacks.h index 578aff45e5..45456f2a1c 100644 --- a/compiler/dex/quick_compiler_callbacks.h +++ b/compiler/dex/quick_compiler_callbacks.h @@ -54,7 +54,7 @@ class QuickCompilerCallbacks FINAL : public CompilerCallbacks { verification_results_ = verification_results; } - bool CanAssumeVerified(ClassReference ref) OVERRIDE; + ClassStatus GetPreviousClassState(ClassReference ref) OVERRIDE; void SetDoesClassUnloading(bool does_class_unloading, CompilerDriver* compiler_driver) OVERRIDE { diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index ee36a92c17..18b54eefba 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -3053,10 +3053,4 @@ void CompilerDriver::SetDexFilesForOatFile(const std::vector<const DexFile*>& de } } -bool CompilerDriver::CanAssumeVerified(ClassReference ref) const { - mirror::Class::Status existing = mirror::Class::kStatusNotReady; - compiled_classes_.Get(DexFileReference(ref.first, ref.second), &existing); - return existing >= mirror::Class::kStatusVerified; -} - } // namespace art diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 11808c1be4..d08d9d7940 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -379,8 +379,6 @@ class CompilerDriver { return profile_compilation_info_; } - bool CanAssumeVerified(ClassReference ref) const; - // Is `boot_image_filename` the name of a core image (small boot // image used for ART testing only)? static bool IsCoreImageFilename(const std::string& boot_image_filename) { diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index 392d57c0f2..278358b250 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -369,8 +369,6 @@ TEST_F(CompilerDriverVerifyTest, VerifyCompilation) { // Test that a class of status kStatusRetryVerificationAtRuntime is indeed recorded that way in the // driver. -// Test that checks that classes can be assumed as verified if unloading mode is enabled and -// the class status is at least verified. TEST_F(CompilerDriverVerifyTest, RetryVerifcationStatusCheckVerified) { Thread* const self = Thread::Current(); jobject class_loader; @@ -401,12 +399,6 @@ TEST_F(CompilerDriverVerifyTest, RetryVerifcationStatusCheckVerified) { mirror::Class::Status status = {}; ASSERT_TRUE(compiler_driver_->GetCompiledClass(ref, &status)); EXPECT_EQ(status, expected_status); - - // Check that we can assume verified if we are a status that is at least verified. - if (status >= mirror::Class::kStatusVerified) { - // Check that the class can be assumed as verified in the compiler driver. - EXPECT_TRUE(callbacks_->CanAssumeVerified(ref)) << status; - } } } diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 6fbb2bd441..58b6137c7a 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -1943,20 +1943,15 @@ void OatWriter::InitBssLayout(InstructionSet instruction_set) { DCHECK_EQ(bss_size_, 0u); if (HasBootImage()) { DCHECK(bss_string_entries_.empty()); - if (bss_method_entries_.empty() && bss_type_entries_.empty()) { - // Nothing to put to the .bss section. - return; - } + } + if (bss_method_entries_.empty() && + bss_type_entries_.empty() && + bss_string_entries_.empty()) { + // Nothing to put to the .bss section. + return; } - // Allocate space for app dex cache arrays in the .bss section. PointerSize pointer_size = GetInstructionSetPointerSize(instruction_set); - if (!HasBootImage()) { - for (const DexFile* dex_file : *dex_files_) { - DexCacheArraysLayout layout(pointer_size, dex_file); - bss_size_ += layout.Size(); - } - } bss_methods_offset_ = bss_size_; diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64.cc index 9095ecdf16..18a55c8b09 100644 --- a/compiler/optimizing/code_generator_vector_arm64.cc +++ b/compiler/optimizing/code_generator_vector_arm64.cc @@ -27,12 +27,13 @@ namespace arm64 { 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 InstructionCodeGeneratorARM64::VisitVecReplicateScalar(HVecReplicateScalar* } } -void LocationsBuilderARM64::VisitVecSetScalars(HVecSetScalars* instruction) { - LOG(FATAL) << "No SIMD for " << instruction->GetId(); -} - -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 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::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 @@ static void CreateVecUnOpLocations(ArenaAllocator* arena, HVecUnaryOperation* in } } +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 @@ void InstructionCodeGeneratorARM64::VisitVecAbs(HVecAbs* instruction) { break; default: LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); } } @@ -805,6 +878,77 @@ void InstructionCodeGeneratorARM64::VisitVecUShr(HVecUShr* instruction) { } } +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 527691d9d9..7a11dff41e 100644 --- a/compiler/optimizing/code_generator_vector_arm_vixl.cc +++ b/compiler/optimizing/code_generator_vector_arm_vixl.cc @@ -73,19 +73,11 @@ void InstructionCodeGeneratorARMVIXL::VisitVecReplicateScalar(HVecReplicateScala } } -void LocationsBuilderARMVIXL::VisitVecSetScalars(HVecSetScalars* instruction) { - LOG(FATAL) << "No SIMD for " << instruction->GetId(); -} - -void InstructionCodeGeneratorARMVIXL::VisitVecSetScalars(HVecSetScalars* instruction) { +void LocationsBuilderARMVIXL::VisitVecExtractScalar(HVecExtractScalar* 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 @@ static void CreateVecUnOpLocations(ArenaAllocator* arena, HVecUnaryOperation* in } } +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 InstructionCodeGeneratorARMVIXL::VisitVecUShr(HVecUShr* instruction) { } } +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 6bf28ab1a3..c2fbf7f04b 100644 --- a/compiler/optimizing/code_generator_vector_mips.cc +++ b/compiler/optimizing/code_generator_vector_mips.cc @@ -88,19 +88,11 @@ void InstructionCodeGeneratorMIPS::VisitVecReplicateScalar(HVecReplicateScalar* } } -void LocationsBuilderMIPS::VisitVecSetScalars(HVecSetScalars* instruction) { - LOG(FATAL) << "No SIMD for " << instruction->GetId(); -} - -void InstructionCodeGeneratorMIPS::VisitVecSetScalars(HVecSetScalars* instruction) { +void LocationsBuilderMIPS::VisitVecExtractScalar(HVecExtractScalar* 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 @@ static void CreateVecUnOpLocations(ArenaAllocator* arena, HVecUnaryOperation* in } } +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 InstructionCodeGeneratorMIPS::VisitVecUShr(HVecUShr* instruction) { } } +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 75bf7a7cbb..9d3a777c13 100644 --- a/compiler/optimizing/code_generator_vector_mips64.cc +++ b/compiler/optimizing/code_generator_vector_mips64.cc @@ -91,19 +91,11 @@ void InstructionCodeGeneratorMIPS64::VisitVecReplicateScalar(HVecReplicateScalar } } -void LocationsBuilderMIPS64::VisitVecSetScalars(HVecSetScalars* instruction) { - LOG(FATAL) << "No SIMD for " << instruction->GetId(); -} - -void InstructionCodeGeneratorMIPS64::VisitVecSetScalars(HVecSetScalars* instruction) { +void LocationsBuilderMIPS64::VisitVecExtractScalar(HVecExtractScalar* 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 @@ static void CreateVecUnOpLocations(ArenaAllocator* arena, HVecUnaryOperation* in } } +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 InstructionCodeGeneratorMIPS64::VisitVecUShr(HVecUShr* instruction) { } } +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 e7aec76aff..37190f8363 100644 --- a/compiler/optimizing/code_generator_vector_x86.cc +++ b/compiler/optimizing/code_generator_vector_x86.cc @@ -27,23 +27,31 @@ namespace x86 { 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. - locations->AddTemp(Location::RequiresFpuRegister()); + // 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, 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, Location::RequiresFpuRegister()); - locations->SetOut(Location::SameAsFirstInput()); + locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant()) + : Location::RequiresFpuRegister()); + locations->SetOut(is_zero ? Location::RequiresFpuRegister() + : Location::SameAsFirstInput()); + break; default: LOG(FATAL) << "Unsupported SIMD type"; @@ -53,46 +61,53 @@ void LocationsBuilderX86::VisitVecReplicateScalar(HVecReplicateScalar* instructi void InstructionCodeGeneratorX86::VisitVecReplicateScalar(HVecReplicateScalar* instruction) { LocationSummary* locations = instruction->GetLocations(); - XmmRegister reg = locations->Out().AsFpuRegister<XmmRegister>(); + 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(reg, locations->InAt(0).AsRegister<Register>()); - __ punpcklbw(reg, reg); - __ punpcklwd(reg, reg); - __ pshufd(reg, reg, Immediate(0)); + __ 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(reg, locations->InAt(0).AsRegister<Register>()); - __ punpcklwd(reg, reg); - __ pshufd(reg, reg, Immediate(0)); + __ 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(reg, locations->InAt(0).AsRegister<Register>()); - __ pshufd(reg, reg, Immediate(0)); + __ 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(reg, locations->InAt(0).AsRegisterPairLow<Register>()); + __ movd(dst, locations->InAt(0).AsRegisterPairLow<Register>()); __ movd(tmp, locations->InAt(0).AsRegisterPairHigh<Register>()); - __ punpckldq(reg, tmp); - __ punpcklqdq(reg, reg); + __ punpckldq(dst, tmp); + __ punpcklqdq(dst, dst); break; } case Primitive::kPrimFloat: DCHECK(locations->InAt(0).Equals(locations->Out())); DCHECK_EQ(4u, instruction->GetVectorLength()); - __ shufps(reg, reg, Immediate(0)); + __ shufps(dst, dst, Immediate(0)); break; case Primitive::kPrimDouble: DCHECK(locations->InAt(0).Equals(locations->Out())); DCHECK_EQ(2u, instruction->GetVectorLength()); - __ shufpd(reg, reg, Immediate(0)); + __ shufpd(dst, dst, Immediate(0)); break; default: LOG(FATAL) << "Unsupported SIMD type"; @@ -100,20 +115,65 @@ void InstructionCodeGeneratorX86::VisitVecReplicateScalar(HVecReplicateScalar* i } } -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 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: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + 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 InstructionCodeGeneratorX86::VisitVecSumReduce(HVecSumReduce* instruction) { - LOG(FATAL) << "No SIMD for " << instruction->GetId(); +void InstructionCodeGeneratorX86::VisitVecExtractScalar(HVecExtractScalar* instruction) { + LocationSummary* locations = instruction->GetLocations(); + XmmRegister src = locations->InAt(0).AsFpuRegister<XmmRegister>(); + 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_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(locations->Out().AsRegisterPairLow<Register>(), src); + __ pshufd(tmp, src, Immediate(1)); + __ movd(locations->Out().AsRegisterPairHigh<Register>(), tmp); + 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. @@ -137,6 +197,73 @@ static void CreateVecUnOpLocations(ArenaAllocator* arena, HVecUnaryOperation* in } } +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 +948,91 @@ void InstructionCodeGeneratorX86::VisitVecUShr(HVecUShr* instruction) { } } +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 +1080,7 @@ static Address VecAddress(LocationSummary* locations, size_t size, bool is_strin 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 +1115,7 @@ void InstructionCodeGeneratorX86::VisitVecLoad(HVecLoad* instruction) { __ 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 c7ee81c60d..7051ba041f 100644 --- a/compiler/optimizing/code_generator_vector_x86_64.cc +++ b/compiler/optimizing/code_generator_vector_x86_64.cc @@ -27,6 +27,8 @@ namespace x86_64 { 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,13 +36,16 @@ void LocationsBuilderX86_64::VisitVecReplicateScalar(HVecReplicateScalar* instru 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, Location::RequiresFpuRegister()); - locations->SetOut(Location::SameAsFirstInput()); + locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant()) + : Location::RequiresFpuRegister()); + locations->SetOut(is_zero ? Location::RequiresFpuRegister() + : Location::SameAsFirstInput()); break; default: LOG(FATAL) << "Unsupported SIMD type"; @@ -50,42 +55,49 @@ void LocationsBuilderX86_64::VisitVecReplicateScalar(HVecReplicateScalar* instru void InstructionCodeGeneratorX86_64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) { LocationSummary* locations = instruction->GetLocations(); - XmmRegister reg = locations->Out().AsFpuRegister<XmmRegister>(); + 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(reg, locations->InAt(0).AsRegister<CpuRegister>()); - __ punpcklbw(reg, reg); - __ punpcklwd(reg, reg); - __ pshufd(reg, reg, Immediate(0)); + __ 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(reg, locations->InAt(0).AsRegister<CpuRegister>()); - __ punpcklwd(reg, reg); - __ pshufd(reg, reg, Immediate(0)); + __ 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(reg, locations->InAt(0).AsRegister<CpuRegister>()); - __ pshufd(reg, reg, Immediate(0)); + __ movd(dst, locations->InAt(0).AsRegister<CpuRegister>()); + __ pshufd(dst, dst, Immediate(0)); break; case Primitive::kPrimLong: DCHECK_EQ(2u, instruction->GetVectorLength()); - __ movd(reg, locations->InAt(0).AsRegister<CpuRegister>()); // is 64-bit - __ punpcklqdq(reg, reg); + __ 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(reg, reg, Immediate(0)); + __ shufps(dst, dst, Immediate(0)); break; case Primitive::kPrimDouble: DCHECK(locations->InAt(0).Equals(locations->Out())); DCHECK_EQ(2u, instruction->GetVectorLength()); - __ shufpd(reg, reg, Immediate(0)); + __ shufpd(dst, dst, Immediate(0)); break; default: LOG(FATAL) << "Unsupported SIMD type"; @@ -93,20 +105,57 @@ void InstructionCodeGeneratorX86_64::VisitVecReplicateScalar(HVecReplicateScalar } } -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 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; + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } } -void InstructionCodeGeneratorX86_64::VisitVecSumReduce(HVecSumReduce* instruction) { - LOG(FATAL) << "No SIMD for " << instruction->GetId(); +void InstructionCodeGeneratorX86_64::VisitVecExtractScalar(HVecExtractScalar* instruction) { + LocationSummary* locations = instruction->GetLocations(); + XmmRegister src = locations->InAt(0).AsFpuRegister<XmmRegister>(); + 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(locations->Out().AsRegister<CpuRegister>(), src); + break; + case Primitive::kPrimLong: + DCHECK_EQ(2u, instruction->GetVectorLength()); + __ movd(locations->Out().AsRegister<CpuRegister>(), src); // is 64-bit + 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. @@ -130,6 +179,73 @@ static void CreateVecUnOpLocations(ArenaAllocator* arena, HVecUnaryOperation* in } } +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 +930,81 @@ void InstructionCodeGeneratorX86_64::VisitVecUShr(HVecUShr* instruction) { } } +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 +1052,7 @@ static Address VecAddress(LocationSummary* locations, size_t size, bool is_strin 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 +1087,7 @@ void InstructionCodeGeneratorX86_64::VisitVecLoad(HVecLoad* instruction) { __ 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/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 027ba7741c..a249cacc93 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -285,6 +285,19 @@ static bool HasReductionFormat(HInstruction* reduction, HInstruction* phi) { 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; @@ -318,8 +331,9 @@ static bool CheckInductionSetFullyRemoved(ArenaSet<HInstruction*>* iset) { HLoopOptimization::HLoopOptimization(HGraph* graph, CompilerDriver* compiler_driver, - HInductionVarAnalysis* induction_analysis) - : HOptimization(graph, kLoopOptimizationPassName), + HInductionVarAnalysis* induction_analysis, + OptimizingCompilerStats* stats) + : HOptimization(graph, kLoopOptimizationPassName, stats), compiler_driver_(compiler_driver), induction_range_(induction_analysis), loop_allocator_(nullptr), @@ -334,7 +348,8 @@ HLoopOptimization::HLoopOptimization(HGraph* graph, 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 +403,14 @@ void HLoopOptimization::LocalRun() { 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 +418,7 @@ void HLoopOptimization::LocalRun() { reductions_ = nullptr; vector_refs_ = nullptr; vector_map_ = nullptr; + vector_permanent_map_ = nullptr; } } @@ -603,11 +622,11 @@ bool HLoopOptimization::OptimizeInnerLoop(LoopNode* node) { // 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); graph_->SetHasSIMD(true); // flag SIMD usage + MaybeRecordStat(stats_, MethodCompilationStat::kLoopVectorized); return true; } return false; @@ -802,6 +821,13 @@ void HLoopOptimization::Vectorize(LoopNode* node, /*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 +867,10 @@ void HLoopOptimization::GenerateNewLoop(LoopNode* node, 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 +887,17 @@ void HLoopOptimization::GenerateNewLoop(LoopNode* node, } } } + // 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 +933,23 @@ bool HLoopOptimization::VectorizeDef(LoopNode* node, } 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 +1005,21 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node, } 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 +1210,14 @@ bool HLoopOptimization::TrySetVectorType(Primitive::Type type, uint64_t* restric 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 +1229,11 @@ bool HLoopOptimization::TrySetVectorType(Primitive::Type type, uint64_t* restric 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 +1242,10 @@ bool HLoopOptimization::TrySetVectorType(Primitive::Type type, uint64_t* restric *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 +1257,12 @@ bool HLoopOptimization::TrySetVectorType(Primitive::Type type, uint64_t* restric 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 +1271,10 @@ bool HLoopOptimization::TrySetVectorType(Primitive::Type type, uint64_t* restric *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 +1286,23 @@ bool HLoopOptimization::TrySetVectorType(Primitive::Type type, uint64_t* restric 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 +1314,23 @@ bool HLoopOptimization::TrySetVectorType(Primitive::Type type, uint64_t* restric 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 +1363,16 @@ void HLoopOptimization::GenerateVecInv(HInstruction* org, Primitive::Type type) 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 +1427,78 @@ void HLoopOptimization::GenerateVecMem(HInstruction* org, 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 +1679,9 @@ bool HLoopOptimization::VectorizeHalvingAddIdiom(LoopNode* node, // 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; @@ -1590,6 +1726,7 @@ bool HLoopOptimization::VectorizeHalvingAddIdiom(LoopNode* node, vector_length_, is_unsigned, is_rounded)); + MaybeRecordStat(stats_, MethodCompilationStat::kLoopVectorizedIdiom); } else { GenerateVecOp(instruction, vector_map_->Get(r), vector_map_->Get(s), type); } diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h index 49be8a3fb4..f34751815b 100644 --- a/compiler/optimizing/loop_optimization.h +++ b/compiler/optimizing/loop_optimization.h @@ -34,7 +34,8 @@ class HLoopOptimization : public HOptimization { public: HLoopOptimization(HGraph* graph, CompilerDriver* compiler_driver, - HInductionVarAnalysis* induction_analysis); + HInductionVarAnalysis* induction_analysis, + OptimizingCompilerStats* stats); void Run() OVERRIDE; @@ -62,17 +63,18 @@ class HLoopOptimization : public HOptimization { * 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 +157,9 @@ class HLoopOptimization : public HOptimization { 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 +258,10 @@ class HLoopOptimization : public HOptimization { // 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/loop_optimization_test.cc b/compiler/optimizing/loop_optimization_test.cc index b5b03d8f26..1c5603d00f 100644 --- a/compiler/optimizing/loop_optimization_test.cc +++ b/compiler/optimizing/loop_optimization_test.cc @@ -31,7 +31,7 @@ class LoopOptimizationTest : public CommonCompilerTest { allocator_(&pool_), graph_(CreateGraph(&allocator_)), iva_(new (&allocator_) HInductionVarAnalysis(graph_)), - loop_opt_(new (&allocator_) HLoopOptimization(graph_, nullptr, iva_)) { + loop_opt_(new (&allocator_) HLoopOptimization(graph_, nullptr, iva_, nullptr)) { BuildGraph(); } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index f60d532c37..869fdd4182 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1374,7 +1374,8 @@ class HLoopInformationOutwardIterator : public ValueObject { 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 @@ inline bool IsInt64AndGet(HInstruction* instruction, /*out*/ int64_t* value) { 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 6261171a00..886d75e5c7 100644 --- a/compiler/optimizing/nodes_vector.h +++ b/compiler/optimizing/nodes_vector.h @@ -63,6 +63,10 @@ class Alignment { // 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 @@ class HVecOperation : public HVariableInputSizeInstruction { 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 @@ class HVecMemoryOperation : public HVecOperation { 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 @@ class HVecReplicateScalar FINAL : public HVecUnaryOperation { 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 @@ class HVecUShr FINAL : public HVecBinaryOperation { // // 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 0238ea4602..5a56a2c210 100644 --- a/compiler/optimizing/nodes_vector_test.cc +++ b/compiler/optimizing/nodes_vector_test.cc @@ -332,4 +332,32 @@ TEST_F(NodesVectorTest, VectorOperationMattersOnMultiplyAccumulate) { 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/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index e98c97cf9a..71d91ae38f 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -509,7 +509,7 @@ static HOptimization* BuildOptimization( } else if (opt_name == SideEffectsAnalysis::kSideEffectsAnalysisPassName) { return new (arena) SideEffectsAnalysis(graph); } else if (opt_name == HLoopOptimization::kLoopOptimizationPassName) { - return new (arena) HLoopOptimization(graph, driver, most_recent_induction); + return new (arena) HLoopOptimization(graph, driver, most_recent_induction, stats); } else if (opt_name == CHAGuardOptimization::kCHAGuardOptimizationPassName) { return new (arena) CHAGuardOptimization(graph); } else if (opt_name == CodeSinking::kCodeSinkingPassName) { @@ -770,7 +770,7 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph, LICM* licm = new (arena) LICM(graph, *side_effects1, stats); HInductionVarAnalysis* induction = new (arena) HInductionVarAnalysis(graph); BoundsCheckElimination* bce = new (arena) BoundsCheckElimination(graph, *side_effects1, induction); - HLoopOptimization* loop = new (arena) HLoopOptimization(graph, driver, induction); + HLoopOptimization* loop = new (arena) HLoopOptimization(graph, driver, induction, stats); LoadStoreAnalysis* lsa = new (arena) LoadStoreAnalysis(graph); LoadStoreElimination* lse = new (arena) LoadStoreElimination(graph, *side_effects2, *lsa, stats); HSharpening* sharpening = new (arena) HSharpening( diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index d6da73cc1c..ff49056798 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -63,6 +63,8 @@ enum MethodCompilationStat { kBooleanSimplified, kIntrinsicRecognized, kLoopInvariantMoved, + kLoopVectorized, + kLoopVectorizedIdiom, kSelectGenerated, kRemovedInstanceOf, kInlinedInvokeVirtualOrInterface, @@ -183,6 +185,8 @@ class OptimizingCompilerStats { case kBooleanSimplified : name = "BooleanSimplified"; break; case kIntrinsicRecognized : name = "IntrinsicRecognized"; break; case kLoopInvariantMoved : name = "LoopInvariantMoved"; break; + case kLoopVectorized : name = "LoopVectorized"; break; + case kLoopVectorizedIdiom : name = "LoopVectorizedIdiom"; break; case kSelectGenerated : name = "SelectGenerated"; break; case kRemovedInstanceOf: name = "RemovedInstanceOf"; break; case kInlinedInvokeVirtualOrInterface: name = "InlinedInvokeVirtualOrInterface"; break; diff --git a/compiler/optimizing/scheduler_arm64.cc b/compiler/optimizing/scheduler_arm64.cc index 510619faf9..1d9d28ab24 100644 --- a/compiler/optimizing/scheduler_arm64.cc +++ b/compiler/optimizing/scheduler_arm64.cc @@ -215,12 +215,12 @@ void SchedulingLatencyVisitorARM64::VisitVecReplicateScalar( 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 @@ void SchedulingLatencyVisitorARM64::VisitVecAnd(HVecAnd* instr ATTRIBUTE_UNUSED) 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 @@ void SchedulingLatencyVisitorARM64::VisitVecUShr(HVecUShr* instr) { 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 63d5b7d6b6..e1a80ec6fb 100644 --- a/compiler/optimizing/scheduler_arm64.h +++ b/compiler/optimizing/scheduler_arm64.h @@ -83,8 +83,8 @@ class SchedulingLatencyVisitorARM64 : public SchedulingLatencyVisitor { 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 @@ class SchedulingLatencyVisitorARM64 : public SchedulingLatencyVisitor { M(VecShl , unused) \ M(VecShr , unused) \ M(VecUShr , unused) \ + M(VecSetScalars , unused) \ M(VecMultiplyAccumulate, unused) \ M(VecLoad , unused) \ M(VecStore , unused) diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc index 6c0d492be6..277f611eb7 100644 --- a/openjdkjvmti/OpenjdkJvmTi.cc +++ b/openjdkjvmti/OpenjdkJvmTi.cc @@ -810,11 +810,11 @@ class JvmtiFunctions { } static jvmtiError GetObjectMonitorUsage(jvmtiEnv* env, - jobject object ATTRIBUTE_UNUSED, - jvmtiMonitorUsage* info_ptr ATTRIBUTE_UNUSED) { + jobject object, + jvmtiMonitorUsage* info_ptr) { ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_get_monitor_info); - return ERR(NOT_IMPLEMENTED); + return ObjectUtil::GetObjectMonitorUsage(env, object, info_ptr); } static jvmtiError GetFieldName(jvmtiEnv* env, diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h index d74e25b807..d3f52f638d 100644 --- a/openjdkjvmti/art_jvmti.h +++ b/openjdkjvmti/art_jvmti.h @@ -226,7 +226,7 @@ const jvmtiCapabilities kPotentialCapabilities = { .can_get_synthetic_attribute = 1, .can_get_owned_monitor_info = 1, .can_get_current_contended_monitor = 0, - .can_get_monitor_info = 0, + .can_get_monitor_info = 1, .can_pop_frame = 0, .can_redefine_classes = 1, .can_signal_thread = 0, @@ -236,7 +236,7 @@ const jvmtiCapabilities kPotentialCapabilities = { .can_access_local_variables = 1, .can_maintain_original_method_order = 1, .can_generate_single_step_events = 1, - .can_generate_exception_events = 0, + .can_generate_exception_events = 1, .can_generate_frame_pop_events = 1, .can_generate_breakpoint_events = 1, .can_suspend = 1, diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index acef682a13..c41e15eaa8 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -48,6 +48,7 @@ #include "nativehelper/ScopedLocalRef.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" +#include "stack.h" #include "thread-inl.h" #include "thread_list.h" #include "ti_phase.h" @@ -552,10 +553,123 @@ class JvmtiMethodTraceListener FINAL : public art::instrumentation::Instrumentat } } + static void FindCatchMethodsFromThrow(art::Thread* self, + art::Handle<art::mirror::Throwable> exception, + /*out*/ art::ArtMethod** out_method, + /*out*/ uint32_t* dex_pc) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + // Finds the location where this exception will most likely be caught. We ignore intervening + // native frames (which could catch the exception) and return the closest java frame with a + // compatible catch statement. + class CatchLocationFinder FINAL : public art::StackVisitor { + public: + CatchLocationFinder(art::Thread* target, + art::Handle<art::mirror::Class> exception_class, + art::Context* context, + /*out*/ art::ArtMethod** out_catch_method, + /*out*/ uint32_t* out_catch_pc) + REQUIRES_SHARED(art::Locks::mutator_lock_) + : StackVisitor(target, context, art::StackVisitor::StackWalkKind::kIncludeInlinedFrames), + exception_class_(exception_class), + catch_method_ptr_(out_catch_method), + catch_dex_pc_ptr_(out_catch_pc) {} + + bool VisitFrame() OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { + art::ArtMethod* method = GetMethod(); + DCHECK(method != nullptr); + if (method->IsRuntimeMethod()) { + return true; + } + + if (!method->IsNative()) { + uint32_t cur_dex_pc = GetDexPc(); + if (cur_dex_pc == art::DexFile::kDexNoIndex) { + // This frame looks opaque. Just keep on going. + return true; + } + bool has_no_move_exception = false; + uint32_t found_dex_pc = method->FindCatchBlock( + exception_class_, cur_dex_pc, &has_no_move_exception); + if (found_dex_pc != art::DexFile::kDexNoIndex) { + // We found the catch. Store the result and return. + *catch_method_ptr_ = method; + *catch_dex_pc_ptr_ = found_dex_pc; + return false; + } + } + return true; + } + + private: + art::Handle<art::mirror::Class> exception_class_; + art::ArtMethod** catch_method_ptr_; + uint32_t* catch_dex_pc_ptr_; + + DISALLOW_COPY_AND_ASSIGN(CatchLocationFinder); + }; + + art::StackHandleScope<1> hs(self); + *out_method = nullptr; + *dex_pc = 0; + std::unique_ptr<art::Context> context(art::Context::Create()); + + CatchLocationFinder clf(self, + hs.NewHandle(exception->GetClass()), + context.get(), + /*out*/ out_method, + /*out*/ dex_pc); + clf.WalkStack(/* include_transitions */ false); + } + // Call-back when an exception is thrown. - void ExceptionThrown(art::Thread* self ATTRIBUTE_UNUSED, - art::Handle<art::mirror::Throwable> exception_object ATTRIBUTE_UNUSED) + void ExceptionThrown(art::Thread* self, art::Handle<art::mirror::Throwable> exception_object) + REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE { + DCHECK(self->IsExceptionThrownByCurrentMethod(exception_object.Get())); + // The instrumentation events get rid of this for us. + DCHECK(!self->IsExceptionPending()); + if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kException)) { + art::JNIEnvExt* jnienv = self->GetJniEnv(); + art::ArtMethod* catch_method; + uint32_t catch_pc; + FindCatchMethodsFromThrow(self, exception_object, &catch_method, &catch_pc); + uint32_t dex_pc = 0; + art::ArtMethod* method = self->GetCurrentMethod(&dex_pc, + /* check_suspended */ true, + /* abort_on_error */ art::kIsDebugBuild); + ScopedLocalRef<jobject> exception(jnienv, + AddLocalRef<jobject>(jnienv, exception_object.Get())); + RunEventCallback<ArtJvmtiEvent::kException>( + self, + jnienv, + art::jni::EncodeArtMethod(method), + static_cast<jlocation>(dex_pc), + exception.get(), + art::jni::EncodeArtMethod(catch_method), + static_cast<jlocation>(catch_pc)); + } + return; + } + + // Call-back when an exception is handled. + void ExceptionHandled(art::Thread* self, art::Handle<art::mirror::Throwable> exception_object) REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE { + // Since the exception has already been handled there shouldn't be one pending. + DCHECK(!self->IsExceptionPending()); + if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kExceptionCatch)) { + art::JNIEnvExt* jnienv = self->GetJniEnv(); + uint32_t dex_pc; + art::ArtMethod* method = self->GetCurrentMethod(&dex_pc, + /* check_suspended */ true, + /* abort_on_error */ art::kIsDebugBuild); + ScopedLocalRef<jobject> exception(jnienv, + AddLocalRef<jobject>(jnienv, exception_object.Get())); + RunEventCallback<ArtJvmtiEvent::kExceptionCatch>( + self, + jnienv, + art::jni::EncodeArtMethod(method), + static_cast<jlocation>(dex_pc), + exception.get()); + } return; } @@ -598,6 +712,10 @@ static uint32_t GetInstrumentationEventsFor(ArtJvmtiEvent event) { return art::instrumentation::Instrumentation::kDexPcMoved; case ArtJvmtiEvent::kFramePop: return art::instrumentation::Instrumentation::kWatchedFramePop; + case ArtJvmtiEvent::kException: + return art::instrumentation::Instrumentation::kExceptionThrown; + case ArtJvmtiEvent::kExceptionCatch: + return art::instrumentation::Instrumentation::kExceptionHandled; default: LOG(FATAL) << "Unknown event "; return 0; @@ -677,6 +795,8 @@ void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { case ArtJvmtiEvent::kMethodExit: case ArtJvmtiEvent::kFieldAccess: case ArtJvmtiEvent::kFieldModification: + case ArtJvmtiEvent::kException: + case ArtJvmtiEvent::kExceptionCatch: SetupTraceListener(method_trace_listener_.get(), event, enable); return; diff --git a/openjdkjvmti/ti_object.cc b/openjdkjvmti/ti_object.cc index 2506acac3a..89ce35256d 100644 --- a/openjdkjvmti/ti_object.cc +++ b/openjdkjvmti/ti_object.cc @@ -35,6 +35,8 @@ #include "mirror/object-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" +#include "thread_list.h" +#include "ti_thread.h" namespace openjdkjvmti { @@ -73,4 +75,59 @@ jvmtiError ObjectUtil::GetObjectHashCode(jvmtiEnv* env ATTRIBUTE_UNUSED, return ERR(NONE); } +jvmtiError ObjectUtil::GetObjectMonitorUsage( + jvmtiEnv* baseenv, jobject obj, jvmtiMonitorUsage* usage) { + ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(baseenv); + if (obj == nullptr) { + return ERR(INVALID_OBJECT); + } + if (usage == nullptr) { + return ERR(NULL_POINTER); + } + art::Thread* self = art::Thread::Current(); + ThreadUtil::SuspendCheck(self); + art::JNIEnvExt* jni = self->GetJniEnv(); + std::vector<jthread> wait; + std::vector<jthread> notify_wait; + { + art::ScopedObjectAccess soa(self); // Now we know we have the shared lock. + art::ScopedThreadSuspension sts(self, art::kNative); + art::ScopedSuspendAll ssa("GetObjectMonitorUsage", /*long_suspend*/false); + art::ObjPtr<art::mirror::Object> target(self->DecodeJObject(obj)); + // This gets the list of threads trying to lock or wait on the monitor. + art::MonitorInfo info(target.Ptr()); + usage->owner = info.owner_ != nullptr ? + jni->AddLocalReference<jthread>(info.owner_->GetPeerFromOtherThread()) : nullptr; + usage->entry_count = info.entry_count_; + for (art::Thread* thd : info.waiters_) { + // RI seems to consider waiting for notify to be included in those waiting to acquire the + // monitor. We will match this behavior. + notify_wait.push_back(jni->AddLocalReference<jthread>(thd->GetPeerFromOtherThread())); + wait.push_back(jni->AddLocalReference<jthread>(thd->GetPeerFromOtherThread())); + } + { + // Scan all threads to see which are waiting on this particular monitor. + art::MutexLock tll(self, *art::Locks::thread_list_lock_); + for (art::Thread* thd : art::Runtime::Current()->GetThreadList()->GetList()) { + if (thd != info.owner_ && target.Ptr() == thd->GetMonitorEnterObject()) { + wait.push_back(jni->AddLocalReference<jthread>(thd->GetPeerFromOtherThread())); + } + } + } + } + usage->waiter_count = wait.size(); + usage->notify_waiter_count = notify_wait.size(); + jvmtiError ret = CopyDataIntoJvmtiBuffer(env, + reinterpret_cast<const unsigned char*>(wait.data()), + wait.size() * sizeof(jthread), + reinterpret_cast<unsigned char**>(&usage->waiters)); + if (ret != OK) { + return ret; + } + return CopyDataIntoJvmtiBuffer(env, + reinterpret_cast<const unsigned char*>(notify_wait.data()), + notify_wait.size() * sizeof(jthread), + reinterpret_cast<unsigned char**>(&usage->notify_waiters)); +} + } // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_object.h b/openjdkjvmti/ti_object.h index fa3bd0f51a..977ec3950f 100644 --- a/openjdkjvmti/ti_object.h +++ b/openjdkjvmti/ti_object.h @@ -42,6 +42,8 @@ class ObjectUtil { static jvmtiError GetObjectSize(jvmtiEnv* env, jobject object, jlong* size_ptr); static jvmtiError GetObjectHashCode(jvmtiEnv* env, jobject object, jint* hash_code_ptr); + + static jvmtiError GetObjectMonitorUsage(jvmtiEnv* env, jobject object, jvmtiMonitorUsage* usage); }; } // namespace openjdkjvmti diff --git a/runtime/Android.bp b/runtime/Android.bp index 71b803de31..6144869415 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -443,6 +443,7 @@ gensrcs { "base/mutex.h", "debugger.h", "base/unix_file/fd_file.h", + "class_status.h", "dex_file.h", "dex_file_layout.h", "dex_instruction.h", @@ -463,7 +464,6 @@ gensrcs { "jdwp/jdwp.h", "jdwp/jdwp_constants.h", "lock_word.h", - "mirror/class.h", "oat.h", "object_callbacks.h", "process_state.h", diff --git a/runtime/aot_class_linker.cc b/runtime/aot_class_linker.cc index 9396565eee..a62cbec1be 100644 --- a/runtime/aot_class_linker.cc +++ b/runtime/aot_class_linker.cc @@ -17,6 +17,7 @@ #include "aot_class_linker.h" #include "class_reference.h" +#include "class_status.h" #include "compiler_callbacks.h" #include "handle_scope-inl.h" #include "mirror/class-inl.h" @@ -75,10 +76,19 @@ verifier::FailureKind AotClassLinker::PerformClassVerification(Thread* self, std::string* error_msg) { Runtime* const runtime = Runtime::Current(); CompilerCallbacks* callbacks = runtime->GetCompilerCallbacks(); - if (callbacks->CanAssumeVerified(ClassReference(&klass->GetDexFile(), - klass->GetDexClassDefIndex()))) { + ClassStatus old_status = callbacks->GetPreviousClassState( + ClassReference(&klass->GetDexFile(), klass->GetDexClassDefIndex())); + // Was it verified? Report no failure. + if (old_status >= ClassStatus::kStatusVerified) { return verifier::FailureKind::kNoFailure; } + // Does it need to be verified at runtime? Report soft failure. + if (old_status >= ClassStatus::kStatusRetryVerificationAtRuntime) { + // Error messages from here are only reported through -verbose:class. It is not worth it to + // create a message. + return verifier::FailureKind::kSoftFailure; + } + // Do the actual work. return ClassLinker::PerformClassVerification(self, klass, log_level, error_msg); } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index d22482f9e3..efef97517b 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1189,219 +1189,41 @@ class FixupInternVisitor { } }; -// Copies data from one array to another array at the same position -// if pred returns false. If there is a page of continuous data in -// the src array for which pred consistently returns true then -// corresponding page in the dst array will not be touched. -// This should reduce number of allocated physical pages. -template <class T, class NullPred> -static void CopyNonNull(const T* src, size_t count, T* dst, const NullPred& pred) { - for (size_t i = 0; i < count; ++i) { - if (!pred(src[i])) { - dst[i] = src[i]; - } - } -} - -template <typename T> -static void CopyDexCachePairs(const std::atomic<mirror::DexCachePair<T>>* src, - size_t count, - std::atomic<mirror::DexCachePair<T>>* dst) { - DCHECK_NE(count, 0u); - DCHECK(!src[0].load(std::memory_order_relaxed).object.IsNull() || - src[0].load(std::memory_order_relaxed).index != 0u); - for (size_t i = 0; i < count; ++i) { - DCHECK_EQ(dst[i].load(std::memory_order_relaxed).index, 0u); - DCHECK(dst[i].load(std::memory_order_relaxed).object.IsNull()); - mirror::DexCachePair<T> source = src[i].load(std::memory_order_relaxed); - if (source.index != 0u || !source.object.IsNull()) { - dst[i].store(source, std::memory_order_relaxed); - } - } -} - -template <typename T> -static void CopyNativeDexCachePairs(std::atomic<mirror::NativeDexCachePair<T>>* src, - size_t count, - std::atomic<mirror::NativeDexCachePair<T>>* dst, - PointerSize pointer_size) { - DCHECK_NE(count, 0u); - DCHECK(mirror::DexCache::GetNativePairPtrSize(src, 0, pointer_size).object != nullptr || - mirror::DexCache::GetNativePairPtrSize(src, 0, pointer_size).index != 0u); - for (size_t i = 0; i < count; ++i) { - DCHECK_EQ(mirror::DexCache::GetNativePairPtrSize(dst, i, pointer_size).index, 0u); - DCHECK(mirror::DexCache::GetNativePairPtrSize(dst, i, pointer_size).object == nullptr); - mirror::NativeDexCachePair<T> source = - mirror::DexCache::GetNativePairPtrSize(src, i, pointer_size); - if (source.index != 0u || source.object != nullptr) { - mirror::DexCache::SetNativePairPtrSize(dst, i, source, pointer_size); - } - } -} - // new_class_set is the set of classes that were read from the class table section in the image. // If there was no class table section, it is null. // Note: using a class here to avoid having to make ClassLinker internals public. class AppImageClassLoadersAndDexCachesHelper { public: - static bool Update( + static void Update( ClassLinker* class_linker, gc::space::ImageSpace* space, Handle<mirror::ClassLoader> class_loader, Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches, - ClassTable::ClassSet* new_class_set, - bool* out_forward_dex_cache_array, - std::string* out_error_msg) + ClassTable::ClassSet* new_class_set) REQUIRES(!Locks::dex_lock_) REQUIRES_SHARED(Locks::mutator_lock_); }; -bool AppImageClassLoadersAndDexCachesHelper::Update( +void AppImageClassLoadersAndDexCachesHelper::Update( ClassLinker* class_linker, gc::space::ImageSpace* space, Handle<mirror::ClassLoader> class_loader, Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches, - ClassTable::ClassSet* new_class_set, - bool* out_forward_dex_cache_array, - std::string* out_error_msg) + ClassTable::ClassSet* new_class_set) REQUIRES(!Locks::dex_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(out_forward_dex_cache_array != nullptr); - DCHECK(out_error_msg != nullptr); - PointerSize image_pointer_size = class_linker->GetImagePointerSize(); Thread* const self = Thread::Current(); gc::Heap* const heap = Runtime::Current()->GetHeap(); const ImageHeader& header = space->GetImageHeader(); { - // Add image classes into the class table for the class loader, and fixup the dex caches and - // class loader fields. + // Register dex caches with the class loader. WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); - // Dex cache array fixup is all or nothing, we must reject app images that have mixed since we - // rely on clobering the dex cache arrays in the image to forward to bss. - size_t num_dex_caches_with_bss_arrays = 0; const size_t num_dex_caches = dex_caches->GetLength(); for (size_t i = 0; i < num_dex_caches; i++) { - ObjPtr<mirror::DexCache> const dex_cache = dex_caches->Get(i); - const DexFile* const dex_file = dex_cache->GetDexFile(); - const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile(); - if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) { - ++num_dex_caches_with_bss_arrays; - } - } - *out_forward_dex_cache_array = num_dex_caches_with_bss_arrays != 0; - if (*out_forward_dex_cache_array) { - if (num_dex_caches_with_bss_arrays != num_dex_caches) { - // Reject application image since we cannot forward only some of the dex cache arrays. - // TODO: We could get around this by having a dedicated forwarding slot. It should be an - // uncommon case. - *out_error_msg = StringPrintf("Dex caches in bss does not match total: %zu vs %zu", - num_dex_caches_with_bss_arrays, - num_dex_caches); - return false; - } - } - - // Only add the classes to the class loader after the points where we can return false. - for (size_t i = 0; i < num_dex_caches; i++) { ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i); const DexFile* const dex_file = dex_cache->GetDexFile(); - const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile(); - if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) { - // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and - // copy over the arrays. - DCHECK(dex_file != nullptr); - size_t num_strings = mirror::DexCache::kDexCacheStringCacheSize; - if (dex_file->NumStringIds() < num_strings) { - num_strings = dex_file->NumStringIds(); - } - size_t num_types = mirror::DexCache::kDexCacheTypeCacheSize; - if (dex_file->NumTypeIds() < num_types) { - num_types = dex_file->NumTypeIds(); - } - size_t num_methods = mirror::DexCache::kDexCacheMethodCacheSize; - if (dex_file->NumMethodIds() < num_methods) { - num_methods = dex_file->NumMethodIds(); - } - size_t num_fields = mirror::DexCache::kDexCacheFieldCacheSize; - if (dex_file->NumFieldIds() < num_fields) { - num_fields = dex_file->NumFieldIds(); - } - size_t num_method_types = mirror::DexCache::kDexCacheMethodTypeCacheSize; - if (dex_file->NumProtoIds() < num_method_types) { - num_method_types = dex_file->NumProtoIds(); - } - const size_t num_call_sites = dex_file->NumCallSiteIds(); - CHECK_EQ(num_strings, dex_cache->NumStrings()); - CHECK_EQ(num_types, dex_cache->NumResolvedTypes()); - CHECK_EQ(num_methods, dex_cache->NumResolvedMethods()); - CHECK_EQ(num_fields, dex_cache->NumResolvedFields()); - CHECK_EQ(num_method_types, dex_cache->NumResolvedMethodTypes()); - CHECK_EQ(num_call_sites, dex_cache->NumResolvedCallSites()); - DexCacheArraysLayout layout(image_pointer_size, dex_file); - uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays(); - if (num_strings != 0u) { - mirror::StringDexCacheType* const image_resolved_strings = dex_cache->GetStrings(); - mirror::StringDexCacheType* const strings = - reinterpret_cast<mirror::StringDexCacheType*>(raw_arrays + layout.StringsOffset()); - CopyDexCachePairs(image_resolved_strings, num_strings, strings); - dex_cache->SetStrings(strings); - } - if (num_types != 0u) { - mirror::TypeDexCacheType* const image_resolved_types = dex_cache->GetResolvedTypes(); - mirror::TypeDexCacheType* const types = - reinterpret_cast<mirror::TypeDexCacheType*>(raw_arrays + layout.TypesOffset()); - CopyDexCachePairs(image_resolved_types, num_types, types); - dex_cache->SetResolvedTypes(types); - } - if (num_methods != 0u) { - mirror::MethodDexCacheType* const image_resolved_methods = - dex_cache->GetResolvedMethods(); - mirror::MethodDexCacheType* const methods = - reinterpret_cast<mirror::MethodDexCacheType*>(raw_arrays + layout.MethodsOffset()); - CopyNativeDexCachePairs(image_resolved_methods, num_methods, methods, image_pointer_size); - dex_cache->SetResolvedMethods(methods); - } - if (num_fields != 0u) { - mirror::FieldDexCacheType* const image_resolved_fields = dex_cache->GetResolvedFields(); - mirror::FieldDexCacheType* const fields = - reinterpret_cast<mirror::FieldDexCacheType*>(raw_arrays + layout.FieldsOffset()); - CopyNativeDexCachePairs(image_resolved_fields, num_fields, fields, image_pointer_size); - dex_cache->SetResolvedFields(fields); - } - if (num_method_types != 0u) { - // NOTE: We currently (Sep 2016) do not resolve any method types at - // compile time, but plan to in the future. This code exists for the - // sake of completeness. - mirror::MethodTypeDexCacheType* const image_resolved_method_types = - dex_cache->GetResolvedMethodTypes(); - mirror::MethodTypeDexCacheType* const method_types = - reinterpret_cast<mirror::MethodTypeDexCacheType*>( - raw_arrays + layout.MethodTypesOffset()); - CopyDexCachePairs(image_resolved_method_types, num_method_types, method_types); - dex_cache->SetResolvedMethodTypes(method_types); - } - if (num_call_sites != 0u) { - GcRoot<mirror::CallSite>* const image_resolved_call_sites = - dex_cache->GetResolvedCallSites(); - GcRoot<mirror::CallSite>* const call_sites = - reinterpret_cast<GcRoot<mirror::CallSite>*>(raw_arrays + layout.CallSitesOffset()); - for (size_t j = 0; kIsDebugBuild && j < num_call_sites; ++j) { - DCHECK(call_sites[j].IsNull()); - } - CopyNonNull(image_resolved_call_sites, - num_call_sites, - call_sites, - [](const GcRoot<mirror::CallSite>& elem) { - return elem.IsNull(); - }); - dex_cache->SetResolvedCallSites(call_sites); - } - } { WriterMutexLock mu2(self, *Locks::dex_lock_); - // Make sure to do this after we update the arrays since we store the resolved types array - // in DexCacheData in RegisterDexFileLocked. We need the array pointer to be the one in the - // BSS. CHECK(!class_linker->FindDexCacheDataLocked(*dex_file).IsValid()); class_linker->RegisterDexFileLocked(*dex_file, dex_cache, class_loader.Get()); } @@ -1468,7 +1290,6 @@ bool AppImageClassLoadersAndDexCachesHelper::Update( VerifyDeclaringClassVisitor visitor; header.VisitPackedArtMethods(&visitor, space->Begin(), kRuntimePointerSize); } - return true; } // Update the class loader. Should only be used on classes in the image space. @@ -1947,7 +1768,7 @@ bool ClassLinker::AddImageSpace( // If we have a class table section, read it and use it for verification in // UpdateAppImageClassLoadersAndDexCaches. ClassTable::ClassSet temp_set; - const ImageSection& class_table_section = header.GetImageSection(ImageHeader::kSectionClassTable); + const ImageSection& class_table_section = header.GetClassTableSection(); const bool added_class_table = class_table_section.Size() > 0u; if (added_class_table) { const uint64_t start_time2 = NanoTime(); @@ -1958,37 +1779,17 @@ bool ClassLinker::AddImageSpace( VLOG(image) << "Adding class table classes took " << PrettyDuration(NanoTime() - start_time2); } if (app_image) { - bool forward_dex_cache_arrays = false; - if (!AppImageClassLoadersAndDexCachesHelper::Update(this, - space, - class_loader, - dex_caches, - &temp_set, - /*out*/&forward_dex_cache_arrays, - /*out*/error_msg)) { - return false; - } + AppImageClassLoadersAndDexCachesHelper::Update(this, + space, + class_loader, + dex_caches, + &temp_set); // Update class loader and resolved strings. If added_class_table is false, the resolved // strings were forwarded UpdateAppImageClassLoadersAndDexCaches. UpdateClassLoaderVisitor visitor(space, class_loader.Get()); for (const ClassTable::TableSlot& root : temp_set) { visitor(root.Read()); } - // forward_dex_cache_arrays is true iff we copied all of the dex cache arrays into the .bss. - // In this case, madvise away the dex cache arrays section of the image to reduce RAM usage and - // mark as PROT_NONE to catch any invalid accesses. - if (forward_dex_cache_arrays) { - const ImageSection& dex_cache_section = header.GetDexCacheArraysSection(); - uint8_t* section_begin = AlignUp(space->Begin() + dex_cache_section.Offset(), kPageSize); - uint8_t* section_end = AlignDown(space->Begin() + dex_cache_section.End(), kPageSize); - if (section_begin < section_end) { - madvise(section_begin, section_end - section_begin, MADV_DONTNEED); - mprotect(section_begin, section_end - section_begin, PROT_NONE); - VLOG(image) << "Released and protected dex cache array image section from " - << reinterpret_cast<const void*>(section_begin) << "-" - << reinterpret_cast<const void*>(section_end); - } - } } if (!oat_file->GetBssGcRoots().empty()) { // Insert oat file to class table for visiting .bss GC roots. @@ -7951,7 +7752,8 @@ ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, // We have a valid method from the DexCache but we need to perform ICCE and IAE checks. DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex(); klass = LookupResolvedType(dex_file, method_id.class_idx_, dex_cache.Get(), class_loader.Get()); - DCHECK(klass != nullptr); + CHECK(klass != nullptr) << resolved->PrettyMethod() << " " << resolved << " " + << resolved->GetAccessFlags(); } else { // The method was not in the DexCache, resolve the declaring class. klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader); diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index 07afedf649..56573f550e 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -632,6 +632,10 @@ std::unique_ptr<ClassLoaderContext> ClassLoaderContext::CreateContextForClassLoa } } +static bool IsAbsoluteLocation(const std::string& location) { + return !location.empty() && location[0] == '/'; +} + bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec) const { ClassLoaderContext expected_context; if (!expected_context.Parse(context_spec, /*parse_checksums*/ true)) { @@ -673,18 +677,52 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex DCHECK_EQ(expected_info.classpath.size(), expected_info.checksums.size()); for (size_t k = 0; k < info.classpath.size(); k++) { - if (info.classpath[k] != expected_info.classpath[k]) { + // Compute the dex location that must be compared. + // We shouldn't do a naive comparison `info.classpath[k] == expected_info.classpath[k]` + // because even if they refer to the same file, one could be encoded as a relative location + // and the other as an absolute one. + bool is_dex_name_absolute = IsAbsoluteLocation(info.classpath[k]); + bool is_expected_dex_name_absolute = IsAbsoluteLocation(expected_info.classpath[k]); + std::string dex_name; + std::string expected_dex_name; + + if (is_dex_name_absolute == is_expected_dex_name_absolute) { + // If both locations are absolute or relative then compare them as they are. + // This is usually the case for: shared libraries and secondary dex files. + dex_name = info.classpath[k]; + expected_dex_name = expected_info.classpath[k]; + } else if (is_dex_name_absolute) { + // The runtime name is absolute but the compiled name (the expected one) is relative. + // This is the case for split apks which depend on base or on other splits. + dex_name = info.classpath[k]; + expected_dex_name = OatFile::ResolveRelativeEncodedDexLocation( + info.classpath[k].c_str(), expected_info.classpath[k]); + } else { + // The runtime name is relative but the compiled name is absolute. + // There is no expected use case that would end up here as dex files are always loaded + // with their absolute location. However, be tolerant and do the best effort (in case + // there are unexpected new use case...). + DCHECK(is_expected_dex_name_absolute); + dex_name = OatFile::ResolveRelativeEncodedDexLocation( + expected_info.classpath[k].c_str(), info.classpath[k]); + expected_dex_name = expected_info.classpath[k]; + } + + // Compare the locations. + if (dex_name != expected_dex_name) { LOG(WARNING) << "ClassLoaderContext classpath element mismatch for position " << i << ". expected=" << expected_info.classpath[k] << ", found=" << info.classpath[k] << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; return false; } + + // Compare the checksums. if (info.checksums[k] != expected_info.checksums[k]) { LOG(WARNING) << "ClassLoaderContext classpath element checksum mismatch for position " << i - << ". expected=" << expected_info.checksums[k] - << ", found=" << info.checksums[k] - << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; + << ". expected=" << expected_info.checksums[k] + << ", found=" << info.checksums[k] + << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")"; return false; } } diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc index ddbb73b5b2..18472743fb 100644 --- a/runtime/class_loader_context_test.cc +++ b/runtime/class_loader_context_test.cc @@ -697,7 +697,17 @@ TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d); - ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile(""))); + std::string context_with_no_base_dir = context->EncodeContextForOatFile(""); + ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_no_base_dir)); + + std::string dex_location = GetTestDexFileName("ForClassLoaderA"); + size_t pos = dex_location.rfind('/'); + ASSERT_NE(std::string::npos, pos); + std::string parent = dex_location.substr(0, pos); + + std::string context_with_base_dir = context->EncodeContextForOatFile(parent); + ASSERT_NE(context_with_base_dir, context_with_no_base_dir); + ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_base_dir)); } TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultidex) { diff --git a/runtime/class_status.h b/runtime/class_status.h new file mode 100644 index 0000000000..0877e68818 --- /dev/null +++ b/runtime/class_status.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_CLASS_STATUS_H_ +#define ART_RUNTIME_CLASS_STATUS_H_ + +#include <iosfwd> + +namespace art { + +// Class Status +// +// kStatusRetired: Class that's temporarily used till class linking time +// has its (vtable) size figured out and has been cloned to one with the +// right size which will be the one used later. The old one is retired and +// will be gc'ed once all refs to the class point to the newly +// cloned version. +// +// kStatusErrorUnresolved, kStatusErrorResolved: Class is erroneous. We need +// to distinguish between classes that have been resolved and classes that +// have not. This is important because the const-class instruction needs to +// return a previously resolved class even if its subsequent initialization +// failed. We also need this to decide whether to wrap a previous +// initialization failure in ClassDefNotFound error or not. +// +// kStatusNotReady: If a Class cannot be found in the class table by +// FindClass, it allocates an new one with AllocClass in the +// kStatusNotReady and calls LoadClass. Note if it does find a +// class, it may not be kStatusResolved and it will try to push it +// forward toward kStatusResolved. +// +// kStatusIdx: LoadClass populates with Class with information from +// the DexFile, moving the status to kStatusIdx, indicating that the +// Class value in super_class_ has not been populated. The new Class +// can then be inserted into the classes table. +// +// kStatusLoaded: After taking a lock on Class, the ClassLinker will +// attempt to move a kStatusIdx class forward to kStatusLoaded by +// using ResolveClass to initialize the super_class_ and ensuring the +// interfaces are resolved. +// +// kStatusResolving: Class is just cloned with the right size from +// temporary class that's acting as a placeholder for linking. The old +// class will be retired. New class is set to this status first before +// moving on to being resolved. +// +// kStatusResolved: Still holding the lock on Class, the ClassLinker +// shows linking is complete and fields of the Class populated by making +// it kStatusResolved. Java allows circularities of the form where a super +// class has a field that is of the type of the sub class. We need to be able +// to fully resolve super classes while resolving types for fields. +// +// kStatusRetryVerificationAtRuntime: The verifier sets a class to +// this state if it encounters a soft failure at compile time. This +// often happens when there are unresolved classes in other dex +// files, and this status marks a class as needing to be verified +// again at runtime. +// +// TODO: Explain the other states +enum ClassStatus { + kStatusRetired = -3, // Retired, should not be used. Use the newly cloned one instead. + kStatusErrorResolved = -2, + kStatusErrorUnresolved = -1, + kStatusNotReady = 0, + kStatusIdx = 1, // Loaded, DEX idx in super_class_type_idx_ and interfaces_type_idx_. + kStatusLoaded = 2, // DEX idx values resolved. + kStatusResolving = 3, // Just cloned from temporary class object. + kStatusResolved = 4, // Part of linking. + kStatusVerifying = 5, // In the process of being verified. + kStatusRetryVerificationAtRuntime = 6, // Compile time verification failed, retry at runtime. + kStatusVerifyingAtRuntime = 7, // Retrying verification at runtime. + kStatusVerified = 8, // Logically part of linking; done pre-init. + kStatusSuperclassValidated = 9, // Superclass validation part of init done. + kStatusInitializing = 10, // Class init in progress. + kStatusInitialized = 11, // Ready to go. + kStatusMax = 12, +}; + +std::ostream& operator<<(std::ostream& os, const ClassStatus& rhs); + +} // namespace art + +#endif // ART_RUNTIME_CLASS_STATUS_H_ diff --git a/runtime/compiler_callbacks.h b/runtime/compiler_callbacks.h index c51bb5e176..9b227141e0 100644 --- a/runtime/compiler_callbacks.h +++ b/runtime/compiler_callbacks.h @@ -19,6 +19,7 @@ #include "base/mutex.h" #include "class_reference.h" +#include "class_status.h" namespace art { @@ -51,8 +52,10 @@ class CompilerCallbacks { virtual verifier::VerifierDeps* GetVerifierDeps() const = 0; virtual void SetVerifierDeps(verifier::VerifierDeps* deps ATTRIBUTE_UNUSED) {} - virtual bool CanAssumeVerified(ClassReference ref ATTRIBUTE_UNUSED) { - return false; + // Return the class status of a previous stage of the compilation. This can be used, for example, + // when class unloading is enabled during multidex compilation. + virtual ClassStatus GetPreviousClassState(ClassReference ref ATTRIBUTE_UNUSED) { + return ClassStatus::kStatusNotReady; } virtual void SetDoesClassUnloading(bool does_class_unloading ATTRIBUTE_UNUSED, diff --git a/runtime/debugger.cc b/runtime/debugger.cc index fe0bad2111..af56810fcb 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -262,6 +262,13 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati << " " << dex_pc; } + // TODO Might be worth it to post ExceptionCatch event. + void ExceptionHandled(Thread* thread ATTRIBUTE_UNUSED, + Handle<mirror::Throwable> throwable ATTRIBUTE_UNUSED) OVERRIDE { + LOG(ERROR) << "Unexpected exception handled event in debugger"; + } + + private: static bool IsReturn(ArtMethod* method, uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) { diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index b3528d8d42..4d8c6872e8 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -110,6 +110,7 @@ Instrumentation::Instrumentation() have_watched_frame_pop_listeners_(false), have_branch_listeners_(false), have_invoke_virtual_or_interface_listeners_(false), + have_exception_handled_listeners_(false), deoptimized_methods_lock_("deoptimized methods lock", kDeoptimizedMethodsLock), deoptimization_enabled_(false), interpreter_handler_table_(kMainHandlerTable), @@ -513,6 +514,11 @@ void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t ev watched_frame_pop_listeners_, listener, &have_watched_frame_pop_listeners_); + PotentiallyAddListenerTo(kExceptionHandled, + events, + exception_handled_listeners_, + listener, + &have_exception_handled_listeners_); UpdateInterpreterHandlerTable(); } @@ -595,6 +601,11 @@ void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t watched_frame_pop_listeners_, listener, &have_watched_frame_pop_listeners_); + PotentiallyRemoveListenerFrom(kExceptionHandled, + events, + exception_handled_listeners_, + listener, + &have_exception_handled_listeners_); UpdateInterpreterHandlerTable(); } @@ -1118,10 +1129,28 @@ void Instrumentation::ExceptionThrownEvent(Thread* thread, listener->ExceptionThrown(thread, h_exception); } } + // See b/65049545 for discussion about this behavior. + thread->AssertNoPendingException(); thread->SetException(h_exception.Get()); } } +void Instrumentation::ExceptionHandledEvent(Thread* thread, + mirror::Throwable* exception_object) const { + Thread* self = Thread::Current(); + StackHandleScope<1> hs(self); + Handle<mirror::Throwable> h_exception(hs.NewHandle(exception_object)); + if (HasExceptionHandledListeners()) { + // We should have cleared the exception so that callers can detect a new one. + DCHECK(thread->GetException() == nullptr); + for (InstrumentationListener* listener : exception_handled_listeners_) { + if (listener != nullptr) { + listener->ExceptionHandled(thread, h_exception); + } + } + } +} + // Computes a frame ID by ignoring inlined frames. size_t Instrumentation::ComputeFrameId(Thread* self, size_t frame_depth, diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index 43e29cffac..5763a41a05 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -131,6 +131,10 @@ struct InstrumentationListener { Handle<mirror::Throwable> exception_object) REQUIRES_SHARED(Locks::mutator_lock_) = 0; + // Call-back when an exception is caught/handled by java code. + virtual void ExceptionHandled(Thread* thread, Handle<mirror::Throwable> exception_object) + REQUIRES_SHARED(Locks::mutator_lock_) = 0; + // Call-back for when we execute a branch. virtual void Branch(Thread* thread, ArtMethod* method, @@ -173,6 +177,7 @@ class Instrumentation { kBranch = 0x80, kInvokeVirtualOrInterface = 0x100, kWatchedFramePop = 0x200, + kExceptionHandled = 0x400, }; enum class InstrumentationLevel { @@ -350,12 +355,16 @@ class Instrumentation { return have_watched_frame_pop_listeners_; } + bool HasExceptionHandledListeners() const REQUIRES_SHARED(Locks::mutator_lock_) { + return have_exception_handled_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_watched_frame_pop_listeners_; + have_watched_frame_pop_listeners_ || have_exception_handled_listeners_; } // Any instrumentation *other* than what is needed for Jit profiling active? @@ -363,7 +372,8 @@ class Instrumentation { 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_watched_frame_pop_listeners_; + have_branch_listeners_ || have_watched_frame_pop_listeners_ || + have_exception_handled_listeners_; } // Inform listeners that a method has been entered. A dex PC is provided as we may install @@ -453,6 +463,11 @@ class Instrumentation { void ExceptionThrownEvent(Thread* thread, mirror::Throwable* exception_object) const REQUIRES_SHARED(Locks::mutator_lock_); + // Inform listeners that an exception has been handled. This is not sent for native code or for + // exceptions which reach the end of the thread's stack. + void ExceptionHandledEvent(Thread* thread, mirror::Throwable* exception_object) const + REQUIRES_SHARED(Locks::mutator_lock_); + // Called when an instrumented method is entered. The intended link register (lr) is saved so // that returning causes a branch to the method exit stub. Generates method enter events. void PushInstrumentationStackFrame(Thread* self, mirror::Object* this_object, @@ -641,6 +656,10 @@ class Instrumentation { // Do we have any invoke listeners? Short-cut to avoid taking the instrumentation_lock_. bool have_invoke_virtual_or_interface_listeners_ GUARDED_BY(Locks::mutator_lock_); + // Do we have any exception handled listeners? Short-cut to avoid taking the + // instrumentation_lock_. + bool have_exception_handled_listeners_ GUARDED_BY(Locks::mutator_lock_); + // Contains the instrumentation level required by each client of the instrumentation identified // by a string key. typedef SafeMap<const char*, InstrumentationLevel> InstrumentationLevelTable; @@ -667,6 +686,7 @@ class Instrumentation { 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_); + std::list<InstrumentationListener*> exception_handled_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 1aa4e91eda..89baa3504f 100644 --- a/runtime/instrumentation_test.cc +++ b/runtime/instrumentation_test.cc @@ -48,6 +48,7 @@ class TestInstrumentationListener FINAL : public instrumentation::Instrumentatio received_field_written_event(false), received_field_written_object_event(false), received_exception_thrown_event(false), + received_exception_handled_event(false), received_branch_event(false), received_invoke_virtual_or_interface_event(false), received_watched_frame_pop(false) {} @@ -131,6 +132,12 @@ class TestInstrumentationListener FINAL : public instrumentation::Instrumentatio received_exception_thrown_event = true; } + void ExceptionHandled(Thread* self ATTRIBUTE_UNUSED, + Handle<mirror::Throwable> throwable ATTRIBUTE_UNUSED) + OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { + received_exception_handled_event = true; + } + void Branch(Thread* thread ATTRIBUTE_UNUSED, ArtMethod* method ATTRIBUTE_UNUSED, uint32_t dex_pc ATTRIBUTE_UNUSED, @@ -163,6 +170,7 @@ class TestInstrumentationListener FINAL : public instrumentation::Instrumentatio received_field_written_event = false; received_field_written_object_event = false; received_exception_thrown_event = false; + received_exception_handled_event = false; received_branch_event = false; received_invoke_virtual_or_interface_event = false; received_watched_frame_pop = false; @@ -177,6 +185,7 @@ class TestInstrumentationListener FINAL : public instrumentation::Instrumentatio bool received_field_written_event; bool received_field_written_object_event; bool received_exception_thrown_event; + bool received_exception_handled_event; bool received_branch_event; bool received_invoke_virtual_or_interface_event; bool received_watched_frame_pop; @@ -369,6 +378,8 @@ class InstrumentationTest : public CommonRuntimeTest { return instr->HasFieldWriteListeners(); case instrumentation::Instrumentation::kExceptionThrown: return instr->HasExceptionThrownListeners(); + case instrumentation::Instrumentation::kExceptionHandled: + return instr->HasExceptionHandledListeners(); case instrumentation::Instrumentation::kBranch: return instr->HasBranchListeners(); case instrumentation::Instrumentation::kInvokeVirtualOrInterface: @@ -429,6 +440,13 @@ class InstrumentationTest : public CommonRuntimeTest { case instrumentation::Instrumentation::kWatchedFramePop: instr->WatchedFramePopped(self, frame); break; + case instrumentation::Instrumentation::kExceptionHandled: { + ThrowArithmeticExceptionDivideByZero(); + mirror::Throwable* event_exception = self->GetException(); + self->ClearException(); + instr->ExceptionHandledEvent(self, event_exception); + break; + } default: LOG(FATAL) << "Unknown instrumentation event " << event_type; UNREACHABLE(); @@ -455,6 +473,8 @@ class InstrumentationTest : public CommonRuntimeTest { (with_object && listener.received_field_written_object_event); case instrumentation::Instrumentation::kExceptionThrown: return listener.received_exception_thrown_event; + case instrumentation::Instrumentation::kExceptionHandled: + return listener.received_exception_handled_event; case instrumentation::Instrumentation::kBranch: return listener.received_branch_event; case instrumentation::Instrumentation::kInvokeVirtualOrInterface: @@ -484,6 +504,7 @@ TEST_F(InstrumentationTest, NoInstrumentation) { // Check there is no registered listener. EXPECT_FALSE(instr->HasDexPcListeners()); EXPECT_FALSE(instr->HasExceptionThrownListeners()); + EXPECT_FALSE(instr->HasExceptionHandledListeners()); EXPECT_FALSE(instr->HasFieldReadListeners()); EXPECT_FALSE(instr->HasFieldWriteListeners()); EXPECT_FALSE(instr->HasMethodEntryListeners()); @@ -603,6 +624,10 @@ TEST_F(InstrumentationTest, FieldWritePrimEvent) { /*with_object*/ false); } +TEST_F(InstrumentationTest, ExceptionHandledEvent) { + TestEvent(instrumentation::Instrumentation::kExceptionHandled); +} + TEST_F(InstrumentationTest, ExceptionThrownEvent) { TestEvent(instrumentation::Instrumentation::kExceptionThrown); } diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index ec183b3c7f..a1f212305f 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -500,10 +500,8 @@ void EnterInterpreterFromDeoptimize(Thread* self, // null Instrumentation*. const instrumentation::Instrumentation* const instrumentation = first ? nullptr : Runtime::Current()->GetInstrumentation(); - uint32_t found_dex_pc = FindNextInstructionFollowingException(self, *shadow_frame, dex_pc, - instrumentation); - new_dex_pc = found_dex_pc; // the dex pc of a matching catch handler - // or DexFile::kDexNoIndex if there is none. + new_dex_pc = MoveToExceptionHandler( + self, *shadow_frame, instrumentation) ? shadow_frame->GetDexPC() : DexFile::kDexNoIndex; } else if (!from_code) { // Deoptimization is not called from code directly. const Instruction* instr = Instruction::At(&code_item->insns_[dex_pc]); diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index ae461fd987..0028b21f94 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -416,35 +416,57 @@ EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimNot) // iput-objec #undef EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL #undef EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL +// We execute any instrumentation events that are triggered by this exception and change the +// shadow_frame's dex_pc to that of the exception handler if there is one in the current method. +// Return true if we should continue executing in the current method and false if we need to go up +// the stack to find an exception handler. // We accept a null Instrumentation* meaning we must not report anything to the instrumentation. -uint32_t FindNextInstructionFollowingException( - Thread* self, ShadowFrame& shadow_frame, uint32_t dex_pc, - const instrumentation::Instrumentation* instrumentation) { +// TODO We should have a better way to skip instrumentation reporting or possibly rethink that +// behavior. +bool MoveToExceptionHandler(Thread* self, + ShadowFrame& shadow_frame, + const instrumentation::Instrumentation* instrumentation) { self->VerifyStack(); StackHandleScope<2> hs(self); Handle<mirror::Throwable> exception(hs.NewHandle(self->GetException())); - if (instrumentation != nullptr && instrumentation->HasExceptionThrownListeners() - && self->IsExceptionThrownByCurrentMethod(exception.Get())) { + if (instrumentation != nullptr && + instrumentation->HasExceptionThrownListeners() && + self->IsExceptionThrownByCurrentMethod(exception.Get())) { + // See b/65049545 for why we don't need to check to see if the exception has changed. instrumentation->ExceptionThrownEvent(self, exception.Get()); } bool clear_exception = false; 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); + hs.NewHandle(exception->GetClass()), shadow_frame.GetDexPC(), &clear_exception); + if (found_dex_pc == DexFile::kDexNoIndex) { + if (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(), + shadow_frame.GetMethod(), + shadow_frame.GetDexPC()); } - // Exception is not caught by the current method. We will unwind to the - // caller. Notify any instrumentation listener. - instrumentation->MethodUnwindEvent(self, shadow_frame.GetThisObject(), - shadow_frame.GetMethod(), dex_pc); + return false; } else { - // Exception is caught in the current method. We will jump to the found_dex_pc. - if (clear_exception) { + shadow_frame.SetDexPC(found_dex_pc); + if (instrumentation != nullptr && instrumentation->HasExceptionHandledListeners()) { + self->ClearException(); + instrumentation->ExceptionHandledEvent(self, exception.Get()); + if (UNLIKELY(self->IsExceptionPending())) { + // Exception handled event threw an exception. Try to find the handler for this one. + return MoveToExceptionHandler(self, shadow_frame, instrumentation); + } else if (!clear_exception) { + self->SetException(exception.Get()); + } + } else if (clear_exception) { self->ClearException(); } + return true; } - return found_dex_pc; } void UnexpectedOpcode(const Instruction* inst, const ShadowFrame& shadow_frame) { diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 4db8184dc0..3ccab853c3 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -462,9 +462,17 @@ static inline int32_t DoSparseSwitch(const Instruction* inst, const ShadowFrame& return 3; } -uint32_t FindNextInstructionFollowingException(Thread* self, ShadowFrame& shadow_frame, - uint32_t dex_pc, const instrumentation::Instrumentation* instrumentation) - REQUIRES_SHARED(Locks::mutator_lock_); +// We execute any instrumentation events triggered by throwing and/or handing the pending exception +// and change the shadow_frames dex_pc to the appropriate exception handler if the current method +// has one. If the exception has been handled and the shadow_frame is now pointing to a catch clause +// we return true. If the current method is unable to handle the exception we return false. +// This function accepts a null Instrumentation* as a way to cause instrumentation events not to be +// reported. +// TODO We might wish to reconsider how we cause some events to be ignored. +bool MoveToExceptionHandler(Thread* self, + ShadowFrame& shadow_frame, + const instrumentation::Instrumentation* instrumentation) + REQUIRES_SHARED(Locks::mutator_lock_); NO_RETURN void UnexpectedOpcode(const Instruction* inst, const ShadowFrame& shadow_frame) __attribute__((cold)) diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index f352960204..69e091b42d 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -30,10 +30,7 @@ namespace interpreter { do { \ DCHECK(self->IsExceptionPending()); \ self->AllowThreadSuspension(); \ - uint32_t found_dex_pc = FindNextInstructionFollowingException(self, shadow_frame, \ - inst->GetDexPc(insns), \ - instr); \ - if (found_dex_pc == DexFile::kDexNoIndex) { \ + if (!MoveToExceptionHandler(self, shadow_frame, instr)) { \ /* Structured locking is to be enforced for abnormal termination, too. */ \ DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame); \ if (interpret_one_instruction) { \ @@ -42,7 +39,8 @@ namespace interpreter { } \ return JValue(); /* Handled in caller. */ \ } else { \ - int32_t displacement = static_cast<int32_t>(found_dex_pc) - static_cast<int32_t>(dex_pc); \ + int32_t displacement = \ + static_cast<int32_t>(shadow_frame.GetDexPC()) - static_cast<int32_t>(dex_pc); \ inst = inst->RelativeAt(displacement); \ } \ } while (false) diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 88254a8cdc..b8a7a2ad3f 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -490,15 +490,7 @@ extern "C" size_t MterpHandleException(Thread* self, ShadowFrame* shadow_frame) DCHECK(self->IsExceptionPending()); const instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation(); - uint32_t found_dex_pc = FindNextInstructionFollowingException(self, *shadow_frame, - shadow_frame->GetDexPC(), - instrumentation); - if (found_dex_pc == DexFile::kDexNoIndex) { - return false; - } - // OK - we can deal with it. Update and continue. - shadow_frame->SetDexPC(found_dex_pc); - return true; + return MoveToExceptionHandler(self, *shadow_frame, instrumentation); } extern "C" void MterpCheckBefore(Thread* self, ShadowFrame* shadow_frame, uint16_t* dex_pc_ptr) diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index c775cf4613..40157c4808 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -47,6 +47,23 @@ using android::base::StringPrintf; GcRoot<Class> Class::java_lang_Class_; +constexpr Class::Status Class::kStatusRetired; +constexpr Class::Status Class::kStatusErrorResolved; +constexpr Class::Status Class::kStatusErrorUnresolved; +constexpr Class::Status Class::kStatusNotReady; +constexpr Class::Status Class::kStatusIdx; +constexpr Class::Status Class::kStatusLoaded; +constexpr Class::Status Class::kStatusResolving; +constexpr Class::Status Class::kStatusResolved; +constexpr Class::Status Class::kStatusVerifying; +constexpr Class::Status Class::kStatusRetryVerificationAtRuntime; +constexpr Class::Status Class::kStatusVerifyingAtRuntime; +constexpr Class::Status Class::kStatusVerified; +constexpr Class::Status Class::kStatusSuperclassValidated; +constexpr Class::Status Class::kStatusInitializing; +constexpr Class::Status Class::kStatusInitialized; +constexpr Class::Status Class::kStatusMax; + void Class::SetClassClass(ObjPtr<Class> java_lang_Class) { CHECK(java_lang_Class_.IsNull()) << java_lang_Class_.Read() diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 250604b2ef..4c9b1464dc 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -21,6 +21,7 @@ #include "base/enums.h" #include "base/iteration_range.h" #include "class_flags.h" +#include "class_status.h" #include "dex_file.h" #include "dex_file_types.h" #include "gc/allocator_type.h" @@ -76,73 +77,27 @@ class MANAGED Class FINAL : public Object { static constexpr uint32_t kPrimitiveTypeSizeShiftShift = 16; static constexpr uint32_t kPrimitiveTypeMask = (1u << kPrimitiveTypeSizeShiftShift) - 1; - // Class Status - // - // kStatusRetired: Class that's temporarily used till class linking time - // has its (vtable) size figured out and has been cloned to one with the - // right size which will be the one used later. The old one is retired and - // will be gc'ed once all refs to the class point to the newly - // cloned version. - // - // kStatusErrorUnresolved, kStatusErrorResolved: Class is erroneous. We need - // to distinguish between classes that have been resolved and classes that - // have not. This is important because the const-class instruction needs to - // return a previously resolved class even if its subsequent initialization - // failed. We also need this to decide whether to wrap a previous - // initialization failure in ClassDefNotFound error or not. - // - // kStatusNotReady: If a Class cannot be found in the class table by - // FindClass, it allocates an new one with AllocClass in the - // kStatusNotReady and calls LoadClass. Note if it does find a - // class, it may not be kStatusResolved and it will try to push it - // forward toward kStatusResolved. - // - // kStatusIdx: LoadClass populates with Class with information from - // the DexFile, moving the status to kStatusIdx, indicating that the - // Class value in super_class_ has not been populated. The new Class - // can then be inserted into the classes table. - // - // kStatusLoaded: After taking a lock on Class, the ClassLinker will - // attempt to move a kStatusIdx class forward to kStatusLoaded by - // using ResolveClass to initialize the super_class_ and ensuring the - // interfaces are resolved. - // - // kStatusResolving: Class is just cloned with the right size from - // temporary class that's acting as a placeholder for linking. The old - // class will be retired. New class is set to this status first before - // moving on to being resolved. - // - // kStatusResolved: Still holding the lock on Class, the ClassLinker - // shows linking is complete and fields of the Class populated by making - // it kStatusResolved. Java allows circularities of the form where a super - // class has a field that is of the type of the sub class. We need to be able - // to fully resolve super classes while resolving types for fields. - // - // kStatusRetryVerificationAtRuntime: The verifier sets a class to - // this state if it encounters a soft failure at compile time. This - // often happens when there are unresolved classes in other dex - // files, and this status marks a class as needing to be verified - // again at runtime. - // - // TODO: Explain the other states - enum Status { - kStatusRetired = -3, // Retired, should not be used. Use the newly cloned one instead. - kStatusErrorResolved = -2, - kStatusErrorUnresolved = -1, - kStatusNotReady = 0, - kStatusIdx = 1, // Loaded, DEX idx in super_class_type_idx_ and interfaces_type_idx_. - kStatusLoaded = 2, // DEX idx values resolved. - kStatusResolving = 3, // Just cloned from temporary class object. - kStatusResolved = 4, // Part of linking. - kStatusVerifying = 5, // In the process of being verified. - kStatusRetryVerificationAtRuntime = 6, // Compile time verification failed, retry at runtime. - kStatusVerifyingAtRuntime = 7, // Retrying verification at runtime. - kStatusVerified = 8, // Logically part of linking; done pre-init. - kStatusSuperclassValidated = 9, // Superclass validation part of init done. - kStatusInitializing = 10, // Class init in progress. - kStatusInitialized = 11, // Ready to go. - kStatusMax = 12, - }; + // Make ClassStatus available as Class::Status. + using Status = ClassStatus; + + // Required for a minimal change. Fix up and remove in a future change. + static constexpr Status kStatusRetired = Status::kStatusRetired; + static constexpr Status kStatusErrorResolved = Status::kStatusErrorResolved; + static constexpr Status kStatusErrorUnresolved = Status::kStatusErrorUnresolved; + static constexpr Status kStatusNotReady = Status::kStatusNotReady; + static constexpr Status kStatusIdx = Status::kStatusIdx; + static constexpr Status kStatusLoaded = Status::kStatusLoaded; + static constexpr Status kStatusResolving = Status::kStatusResolving; + static constexpr Status kStatusResolved = Status::kStatusResolved; + static constexpr Status kStatusVerifying = Status::kStatusVerifying; + static constexpr Status kStatusRetryVerificationAtRuntime = + Status::kStatusRetryVerificationAtRuntime; + static constexpr Status kStatusVerifyingAtRuntime = Status::kStatusVerifyingAtRuntime; + static constexpr Status kStatusVerified = Status::kStatusVerified; + static constexpr Status kStatusSuperclassValidated = Status::kStatusSuperclassValidated; + static constexpr Status kStatusInitializing = Status::kStatusInitializing; + static constexpr Status kStatusInitialized = Status::kStatusInitialized; + static constexpr Status kStatusMax = Status::kStatusMax; template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> Status GetStatus() REQUIRES_SHARED(Locks::mutator_lock_) { @@ -1556,8 +1511,6 @@ class MANAGED Class FINAL : public Object { DISALLOW_IMPLICIT_CONSTRUCTORS(Class); }; -std::ostream& operator<<(std::ostream& os, const Class::Status& rhs); - } // namespace mirror } // namespace art diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc index f6f20ba3ab..2f63dff3be 100644 --- a/runtime/mirror/dex_cache.cc +++ b/runtime/mirror/dex_cache.cc @@ -46,13 +46,10 @@ void DexCache::InitializeDexCache(Thread* self, DexCacheArraysLayout layout(image_pointer_size, dex_file); uint8_t* raw_arrays = nullptr; - const OatDexFile* const oat_dex = dex_file->GetOatDexFile(); - if (oat_dex != nullptr && oat_dex->GetDexCacheArrays() != nullptr) { - raw_arrays = oat_dex->GetDexCacheArrays(); - } else if (dex_file->NumStringIds() != 0u || - dex_file->NumTypeIds() != 0u || - dex_file->NumMethodIds() != 0u || - dex_file->NumFieldIds() != 0u) { + if (dex_file->NumStringIds() != 0u || + dex_file->NumTypeIds() != 0u || + dex_file->NumMethodIds() != 0u || + dex_file->NumFieldIds() != 0u) { static_assert(ArenaAllocator::kAlignment == 8, "Expecting arena alignment of 8."); DCHECK(layout.Alignment() == 8u || layout.Alignment() == 16u); // Zero-initialized. diff --git a/runtime/monitor.cc b/runtime/monitor.cc index 5c63dcad78..80e6ad3d47 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -1498,13 +1498,21 @@ MonitorInfo::MonitorInfo(mirror::Object* obj) : owner_(nullptr), entry_count_(0) break; case LockWord::kThinLocked: owner_ = Runtime::Current()->GetThreadList()->FindThreadByThreadId(lock_word.ThinLockOwner()); + DCHECK(owner_ != nullptr) << "Thin-locked without owner!"; entry_count_ = 1 + lock_word.ThinLockCount(); // Thin locks have no waiters. break; case LockWord::kFatLocked: { Monitor* mon = lock_word.FatLockMonitor(); owner_ = mon->owner_; - entry_count_ = 1 + mon->lock_count_; + // Here it is okay for the owner to be null since we don't reset the LockWord back to + // kUnlocked until we get a GC. In cases where this hasn't happened yet we will have a fat + // lock without an owner. + if (owner_ != nullptr) { + entry_count_ = 1 + mon->lock_count_; + } else { + DCHECK_EQ(mon->lock_count_, 0) << "Monitor is fat-locked without any owner!"; + } for (Thread* waiter = mon->wait_set_; waiter != nullptr; waiter = waiter->GetWaitNext()) { waiters_.push_back(waiter); } diff --git a/runtime/oat.h b/runtime/oat.h index 1d79ed6dea..ab7c42efbe 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: Add dex section layout info to header. - static constexpr uint8_t kOatVersion[] = { '1', '3', '2', '\0' }; + // Last oat version changed reason: Remove DexCache arrays from .bss. + static constexpr uint8_t kOatVersion[] = { '1', '3', '3', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index d9cfa533ca..200681ecb1 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -55,7 +55,6 @@ #include "type_lookup_table.h" #include "utf-inl.h" #include "utils.h" -#include "utils/dex_cache_arrays_layout-inl.h" #include "vdex_file.h" namespace art { @@ -279,36 +278,6 @@ inline static bool ReadOatDexFileData(const OatFile& oat_file, return true; } -static bool FindDexFileMapItem(const uint8_t* dex_begin, - const uint8_t* dex_end, - DexFile::MapItemType map_item_type, - const DexFile::MapItem** result_item) { - *result_item = nullptr; - - const DexFile::Header* header = - BoundsCheckedCast<const DexFile::Header*>(dex_begin, dex_begin, dex_end); - if (nullptr == header) return false; - - if (!DexFile::IsMagicValid(header->magic_)) return true; // Not a dex file, not an error. - - const DexFile::MapList* map_list = - BoundsCheckedCast<const DexFile::MapList*>(dex_begin + header->map_off_, dex_begin, dex_end); - if (nullptr == map_list) return false; - - const DexFile::MapItem* map_item = map_list->list_; - size_t count = map_list->size_; - while (count--) { - if (map_item->type_ == static_cast<uint16_t>(map_item_type)) { - *result_item = map_item; - break; - } - map_item = BoundsCheckedCast<const DexFile::MapItem*>(map_item + 1, dex_begin, dex_end); - if (nullptr == map_item) return false; - } - - return true; -} - bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { if (!GetOatHeader().IsValid()) { std::string cause = GetOatHeader().GetValidationErrorMessage(); @@ -361,7 +330,7 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { (bss_roots_ != nullptr && (bss_roots_ < bss_begin_ || bss_roots_ > bss_end_)) || (bss_methods_ != nullptr && bss_roots_ != nullptr && bss_methods_ > bss_roots_)) { *error_msg = StringPrintf("In oat file '%s' found bss symbol(s) outside .bss or unordered: " - "begin = %p, methods_ = %p, roots = %p, end = %p", + "begin = %p, methods = %p, roots = %p, end = %p", GetLocation().c_str(), bss_begin_, bss_methods_, @@ -370,11 +339,15 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { return false; } - uint8_t* after_arrays = (bss_methods_ != nullptr) ? bss_methods_ : bss_roots_; // May be null. - uint8_t* dex_cache_arrays = (bss_begin_ == after_arrays) ? nullptr : bss_begin_; - uint8_t* dex_cache_arrays_end = - (bss_begin_ == after_arrays) ? nullptr : (after_arrays != nullptr) ? after_arrays : bss_end_; - DCHECK_EQ(dex_cache_arrays != nullptr, dex_cache_arrays_end != nullptr); + if (bss_methods_ != nullptr && bss_methods_ != bss_begin_) { + *error_msg = StringPrintf("In oat file '%s' found unexpected .bss gap before 'oatbssmethods': " + "begin = %p, methods = %p", + GetLocation().c_str(), + bss_begin_, + bss_methods_); + return false; + } + uint32_t dex_file_count = GetOatHeader().GetDexFileCount(); oat_dex_files_storage_.reserve(dex_file_count); for (size_t i = 0; i < dex_file_count; i++) { @@ -609,37 +582,6 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { reinterpret_cast<const DexFile::Header*>(dex_file_pointer)->method_ids_size_); } - uint8_t* current_dex_cache_arrays = nullptr; - if (dex_cache_arrays != nullptr) { - // All DexCache types except for CallSite have their instance counts in the - // DexFile header. For CallSites, we need to read the info from the MapList. - const DexFile::MapItem* call_sites_item = nullptr; - if (!FindDexFileMapItem(DexBegin(), - DexEnd(), - DexFile::MapItemType::kDexTypeCallSiteIdItem, - &call_sites_item)) { - *error_msg = StringPrintf("In oat file '%s' could not read data from truncated DexFile map", - GetLocation().c_str()); - return false; - } - size_t num_call_sites = call_sites_item == nullptr ? 0 : call_sites_item->size_; - DexCacheArraysLayout layout(pointer_size, *header, num_call_sites); - if (layout.Size() != 0u) { - if (static_cast<size_t>(dex_cache_arrays_end - dex_cache_arrays) < layout.Size()) { - *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with " - "truncated dex cache arrays, %zu < %zu.", - GetLocation().c_str(), - i, - dex_file_location.c_str(), - static_cast<size_t>(dex_cache_arrays_end - dex_cache_arrays), - layout.Size()); - return false; - } - current_dex_cache_arrays = dex_cache_arrays; - dex_cache_arrays += layout.Size(); - } - } - std::string canonical_location = DexFile::GetDexCanonicalLocation(dex_file_location.c_str()); // Create the OatDexFile and add it to the owning container. @@ -651,7 +593,6 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { lookup_table_data, method_bss_mapping, class_offsets_pointer, - current_dex_cache_arrays, dex_layout_sections); oat_dex_files_storage_.push_back(oat_dex_file); @@ -664,14 +605,6 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { } } - if (dex_cache_arrays != dex_cache_arrays_end) { - // We expect the bss section to be either empty (dex_cache_arrays and bss_end_ - // both null) or contain just the dex cache arrays and optionally some GC roots. - *error_msg = StringPrintf("In oat file '%s' found unexpected bss size bigger by %zu bytes.", - GetLocation().c_str(), - static_cast<size_t>(bss_end_ - dex_cache_arrays)); - return false; - } return true; } @@ -1379,7 +1312,6 @@ OatFile::OatDexFile::OatDexFile(const OatFile* oat_file, const uint8_t* lookup_table_data, const MethodBssMapping* method_bss_mapping_data, const uint32_t* oat_class_offsets_pointer, - uint8_t* dex_cache_arrays, const DexLayoutSections* dex_layout_sections) : oat_file_(oat_file), dex_file_location_(dex_file_location), @@ -1389,7 +1321,6 @@ OatFile::OatDexFile::OatDexFile(const OatFile* oat_file, lookup_table_data_(lookup_table_data), method_bss_mapping_(method_bss_mapping_data), oat_class_offsets_pointer_(oat_class_offsets_pointer), - dex_cache_arrays_(dex_cache_arrays), dex_layout_sections_(dex_layout_sections) { // Initialize TypeLookupTable. if (lookup_table_data_ != nullptr) { diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 9a7fe51e8e..e13126bae3 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -422,10 +422,6 @@ class OatDexFile FINAL { // Returns the offset to the OatClass information. Most callers should use GetOatClass. uint32_t GetOatClassOffset(uint16_t class_def_index) const; - uint8_t* GetDexCacheArrays() const { - return dex_cache_arrays_; - } - const uint8_t* GetLookupTableData() const { return lookup_table_data_; } @@ -470,7 +466,6 @@ class OatDexFile FINAL { const uint8_t* lookup_table_data, const MethodBssMapping* method_bss_mapping, const uint32_t* oat_class_offsets_pointer, - uint8_t* dex_cache_arrays, const DexLayoutSections* dex_layout_sections); static void AssertAotCompiler(); @@ -483,7 +478,6 @@ class OatDexFile FINAL { const uint8_t* const lookup_table_data_ = nullptr; const MethodBssMapping* const method_bss_mapping_ = nullptr; const uint32_t* const oat_class_offsets_pointer_ = 0u; - uint8_t* const dex_cache_arrays_ = nullptr; mutable std::unique_ptr<TypeLookupTable> lookup_table_; const DexLayoutSections* const dex_layout_sections_ = nullptr; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index a8ccf89cb2..a67a6aaeaa 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -417,6 +417,12 @@ struct AbortState { return; } Thread* self = Thread::Current(); + + // Dump all threads first and then the aborting thread. While this is counter the logical flow, + // it improves the chance of relevant data surviving in the Android logs. + + DumpAllThreads(os, self); + if (self == nullptr) { os << "(Aborting thread was not attached to runtime!)\n"; DumpKernelStack(os, GetTid(), " kernel: ", false); @@ -432,7 +438,6 @@ struct AbortState { } } } - DumpAllThreads(os, self); } // No thread-safety analysis as we do explicitly test for holding the mutator lock. diff --git a/runtime/trace.cc b/runtime/trace.cc index d7673f3563..b30de795a4 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -811,6 +811,12 @@ void Trace::ExceptionThrown(Thread* thread ATTRIBUTE_UNUSED, LOG(ERROR) << "Unexpected exception thrown event in tracing"; } +void Trace::ExceptionHandled(Thread* thread ATTRIBUTE_UNUSED, + Handle<mirror::Throwable> exception_object ATTRIBUTE_UNUSED) + REQUIRES_SHARED(Locks::mutator_lock_) { + LOG(ERROR) << "Unexpected exception thrown event in tracing"; +} + void Trace::Branch(Thread* /*thread*/, ArtMethod* method, uint32_t /*dex_pc*/, int32_t /*dex_pc_offset*/) REQUIRES_SHARED(Locks::mutator_lock_) { diff --git a/runtime/trace.h b/runtime/trace.h index 8b0931df80..49d5b22373 100644 --- a/runtime/trace.h +++ b/runtime/trace.h @@ -181,6 +181,8 @@ class Trace FINAL : public instrumentation::InstrumentationListener { void ExceptionThrown(Thread* thread, Handle<mirror::Throwable> exception_object) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE; + void ExceptionHandled(Thread* thread, Handle<mirror::Throwable> exception_object) + REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE; void Branch(Thread* thread, ArtMethod* method, uint32_t dex_pc, diff --git a/test/1927-exception-event/exception_event.cc b/test/1927-exception-event/exception_event.cc new file mode 100644 index 0000000000..3197bcd1ae --- /dev/null +++ b/test/1927-exception-event/exception_event.cc @@ -0,0 +1,102 @@ +/* + * 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 Test1927ExceptionEvent { + +static void ThrowNative(JNIEnv* env) { + ScopedLocalRef<jclass> exception(env, env->FindClass("art/Test1927$TestException")); + env->ThrowNew(exception.get(), "from native"); +} + +static void CallMethod(JNIEnv* env, jclass test, const char* name) { + jmethodID m = env->GetStaticMethodID(test, name, "()V"); + env->CallStaticVoidMethod(test, m); +} + +static void ClearAndPrintException(JNIEnv* env, jclass test) { + jthrowable e = env->ExceptionOccurred(); + env->ExceptionClear(); + jmethodID m = env->GetStaticMethodID(test, "printException", "(Ljava/lang/Throwable;)V"); + env->CallStaticVoidMethod(test, m, e); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1927_terminal_1N(JNIEnv* env, jclass) { + ThrowNative(env); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1927_test_1N(JNIEnv* env, jclass test) { + ThrowNative(env); + ClearAndPrintException(env, test); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1927_test_1N_1J(JNIEnv* env, jclass test) { + CallMethod(env, test, "terminal_J"); + ClearAndPrintException(env, test); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1927_test_1N_1N(JNIEnv* env, jclass test) { + CallMethod(env, test, "terminal_N"); + ClearAndPrintException(env, test); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1927_intermediate_1N_1J(JNIEnv* env, jclass test) { + CallMethod(env, test, "terminal_J"); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1927_intermediate_1N_1N(JNIEnv* env, jclass test) { + CallMethod(env, test, "terminal_N"); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1927_test_1N_1J_1J(JNIEnv* env, jclass test) { + CallMethod(env, test, "intermediate_J_J"); + ClearAndPrintException(env, test); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1927_test_1N_1J_1N(JNIEnv* env, jclass test) { + CallMethod(env, test, "intermediate_J_N"); + ClearAndPrintException(env, test); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1927_test_1N_1N_1J(JNIEnv* env, jclass test) { + CallMethod(env, test, "intermediate_N_J"); + ClearAndPrintException(env, test); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1927_test_1N_1N_1N(JNIEnv* env, jclass test) { + CallMethod(env, test, "intermediate_N_N"); + ClearAndPrintException(env, test); +} + +} // namespace Test1927ExceptionEvent +} // namespace art diff --git a/test/1927-exception-event/expected.txt b/test/1927-exception-event/expected.txt new file mode 100644 index 0000000000..be8f39c29a --- /dev/null +++ b/test/1927-exception-event/expected.txt @@ -0,0 +1,278 @@ +class art.Test1927$TestException +Running test_J +main: public static void art.Test1927.test_J() @ line = 110 throws class art.Test1927$TestException: from java + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66 + public static void art.Test1927.test_J() @ line = 110 + public static void art.Test1927.run() throws java.lang.Exception @ line = 239 + Will be caught by: public static void art.Test1927.test_J() @ line = 111 +main: public static void art.Test1927.test_J() @ line = 111 caught class art.Test1927$TestException: from java + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 43 + public static void art.Test1927.test_J() @ line = 111 + public static void art.Test1927.run() throws java.lang.Exception @ line = 239 +Caught exception: art.Test1927$TestException: from java +Running test_N() +Caught exception: art.Test1927$TestException: from native +Running test_J_J() +main: public static void art.Test1927.terminal_J() @ line = 103 throws class art.Test1927$TestException: from java + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66 + public static void art.Test1927.terminal_J() @ line = 103 + public static void art.Test1927.test_J_J() @ line = 121 + public static void art.Test1927.run() throws java.lang.Exception @ line = 243 + Will be caught by: public static void art.Test1927.test_J_J() @ line = 122 +main: public static void art.Test1927.test_J_J() @ line = 122 caught class art.Test1927$TestException: from java + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 43 + public static void art.Test1927.test_J_J() @ line = 122 + public static void art.Test1927.run() throws java.lang.Exception @ line = 243 +Caught exception: art.Test1927$TestException: from java +Running test_J_N() +main: public static native void art.Test1927.terminal_N() @ line = -1 throws class art.Test1927$TestException: from native + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66 + public static native void art.Test1927.terminal_N() @ line = -1 + public static void art.Test1927.test_J_N() @ line = 129 + public static void art.Test1927.run() throws java.lang.Exception @ line = 245 + Will be caught by: public static void art.Test1927.test_J_N() @ line = 130 +main: public static void art.Test1927.test_J_N() @ line = 130 caught class art.Test1927$TestException: from native + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 43 + public static void art.Test1927.test_J_N() @ line = 130 + public static void art.Test1927.run() throws java.lang.Exception @ line = 245 +Caught exception: art.Test1927$TestException: from native +Running test_N_J() +main: public static void art.Test1927.terminal_J() @ line = 103 throws class art.Test1927$TestException: from java + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66 + public static void art.Test1927.terminal_J() @ line = 103 + public static native void art.Test1927.test_N_J() @ line = -1 + public static void art.Test1927.run() throws java.lang.Exception @ line = 247 + Will be caught by: <UNKNOWN> +Caught exception: art.Test1927$TestException: from java +Running test_N_N() +main: public static native void art.Test1927.terminal_N() @ line = -1 throws class art.Test1927$TestException: from native + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66 + public static native void art.Test1927.terminal_N() @ line = -1 + public static native void art.Test1927.test_N_N() @ line = -1 + public static void art.Test1927.run() throws java.lang.Exception @ line = 249 + Will be caught by: <UNKNOWN> +Caught exception: art.Test1927$TestException: from native +Running test_J_J_J() +main: public static void art.Test1927.terminal_J() @ line = 103 throws class art.Test1927$TestException: from java + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66 + public static void art.Test1927.terminal_J() @ line = 103 + public static void art.Test1927.intermediate_J_J() @ line = 138 + public static void art.Test1927.test_J_J_J() @ line = 145 + public static void art.Test1927.run() throws java.lang.Exception @ line = 251 + Will be caught by: public static void art.Test1927.test_J_J_J() @ line = 146 +main: public static void art.Test1927.test_J_J_J() @ line = 146 caught class art.Test1927$TestException: from java + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 43 + public static void art.Test1927.test_J_J_J() @ line = 146 + public static void art.Test1927.run() throws java.lang.Exception @ line = 251 +Caught exception: art.Test1927$TestException: from java +Running test_J_J_N() +main: public static native void art.Test1927.terminal_N() @ line = -1 throws class art.Test1927$TestException: from native + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66 + public static native void art.Test1927.terminal_N() @ line = -1 + public static void art.Test1927.intermediate_J_N() @ line = 139 + public static void art.Test1927.test_J_J_N() @ line = 153 + public static void art.Test1927.run() throws java.lang.Exception @ line = 253 + Will be caught by: public static void art.Test1927.test_J_J_N() @ line = 154 +main: public static void art.Test1927.test_J_J_N() @ line = 154 caught class art.Test1927$TestException: from native + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 43 + public static void art.Test1927.test_J_J_N() @ line = 154 + public static void art.Test1927.run() throws java.lang.Exception @ line = 253 +Caught exception: art.Test1927$TestException: from native +Running test_J_N_J() +main: public static void art.Test1927.terminal_J() @ line = 103 throws class art.Test1927$TestException: from java + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66 + public static void art.Test1927.terminal_J() @ line = 103 + public static native void art.Test1927.intermediate_N_J() @ line = -1 + public static void art.Test1927.test_J_N_J() @ line = 161 + public static void art.Test1927.run() throws java.lang.Exception @ line = 255 + Will be caught by: public static void art.Test1927.test_J_N_J() @ line = 162 +main: public static void art.Test1927.test_J_N_J() @ line = 162 caught class art.Test1927$TestException: from java + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 43 + public static void art.Test1927.test_J_N_J() @ line = 162 + public static void art.Test1927.run() throws java.lang.Exception @ line = 255 +Caught exception: art.Test1927$TestException: from java +Running test_J_N_N() +main: public static native void art.Test1927.terminal_N() @ line = -1 throws class art.Test1927$TestException: from native + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66 + public static native void art.Test1927.terminal_N() @ line = -1 + public static native void art.Test1927.intermediate_N_N() @ line = -1 + public static void art.Test1927.test_J_N_N() @ line = 169 + public static void art.Test1927.run() throws java.lang.Exception @ line = 257 + Will be caught by: public static void art.Test1927.test_J_N_N() @ line = 170 +main: public static void art.Test1927.test_J_N_N() @ line = 170 caught class art.Test1927$TestException: from native + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 43 + public static void art.Test1927.test_J_N_N() @ line = 170 + public static void art.Test1927.run() throws java.lang.Exception @ line = 257 +Caught exception: art.Test1927$TestException: from native +Running test_N_J_J() +main: public static void art.Test1927.terminal_J() @ line = 103 throws class art.Test1927$TestException: from java + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66 + public static void art.Test1927.terminal_J() @ line = 103 + public static void art.Test1927.intermediate_J_J() @ line = 138 + public static native void art.Test1927.test_N_J_J() @ line = -1 + public static void art.Test1927.run() throws java.lang.Exception @ line = 259 + Will be caught by: <UNKNOWN> +Caught exception: art.Test1927$TestException: from java +Running test_N_J_N() +main: public static native void art.Test1927.terminal_N() @ line = -1 throws class art.Test1927$TestException: from native + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66 + public static native void art.Test1927.terminal_N() @ line = -1 + public static void art.Test1927.intermediate_J_N() @ line = 139 + public static native void art.Test1927.test_N_J_N() @ line = -1 + public static void art.Test1927.run() throws java.lang.Exception @ line = 261 + Will be caught by: <UNKNOWN> +Caught exception: art.Test1927$TestException: from native +Running test_N_N_J() +main: public static void art.Test1927.terminal_J() @ line = 103 throws class art.Test1927$TestException: from java + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66 + public static void art.Test1927.terminal_J() @ line = 103 + public static native void art.Test1927.intermediate_N_J() @ line = -1 + public static native void art.Test1927.test_N_N_J() @ line = -1 + public static void art.Test1927.run() throws java.lang.Exception @ line = 263 + Will be caught by: <UNKNOWN> +Caught exception: art.Test1927$TestException: from java +Running test_N_N_N() +main: public static native void art.Test1927.terminal_N() @ line = -1 throws class art.Test1927$TestException: from native + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66 + public static native void art.Test1927.terminal_N() @ line = -1 + public static native void art.Test1927.intermediate_N_N() @ line = -1 + public static native void art.Test1927.test_N_N_N() @ line = -1 + public static void art.Test1927.run() throws java.lang.Exception @ line = 265 + Will be caught by: <UNKNOWN> +Caught exception: art.Test1927$TestException: from native +Running test_extra_N_J_J() +main: public static void art.Test1927.terminal_J() @ line = 103 throws class art.Test1927$TestException: from java + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66 + public static void art.Test1927.terminal_J() @ line = 103 + public static void art.Test1927.intermediate_J_J() @ line = 138 + public static native void art.Test1927.test_N_J_J() @ line = -1 + public static void art.Test1927.test_extra_N_J_J() @ line = 182 + public static void art.Test1927.run() throws java.lang.Exception @ line = 267 + Will be caught by: public static void art.Test1927.test_extra_N_J_J() @ line = 183 +Caught exception: art.Test1927$TestException: from java +Running test_extra_N_J_N() +main: public static native void art.Test1927.terminal_N() @ line = -1 throws class art.Test1927$TestException: from native + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66 + public static native void art.Test1927.terminal_N() @ line = -1 + public static void art.Test1927.intermediate_J_N() @ line = 139 + public static native void art.Test1927.test_N_J_N() @ line = -1 + public static void art.Test1927.test_extra_N_J_N() @ line = 189 + public static void art.Test1927.run() throws java.lang.Exception @ line = 269 + Will be caught by: public static void art.Test1927.test_extra_N_J_N() @ line = 190 +Caught exception: art.Test1927$TestException: from native +Running test_extra_N_N_J() +main: public static void art.Test1927.terminal_J() @ line = 103 throws class art.Test1927$TestException: from java + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66 + public static void art.Test1927.terminal_J() @ line = 103 + public static native void art.Test1927.intermediate_N_J() @ line = -1 + public static native void art.Test1927.test_N_N_J() @ line = -1 + public static void art.Test1927.test_extra_N_N_J() @ line = 196 + public static void art.Test1927.run() throws java.lang.Exception @ line = 271 + Will be caught by: public static void art.Test1927.test_extra_N_N_J() @ line = 197 +Caught exception: art.Test1927$TestException: from java +Running test_extra_N_N_N() +main: public static native void art.Test1927.terminal_N() @ line = -1 throws class art.Test1927$TestException: from native + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1927.PrintStack() @ line = 28 + public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66 + public static native void art.Test1927.terminal_N() @ line = -1 + public static native void art.Test1927.intermediate_N_N() @ line = -1 + public static native void art.Test1927.test_N_N_N() @ line = -1 + public static void art.Test1927.test_extra_N_N_N() @ line = 203 + public static void art.Test1927.run() throws java.lang.Exception @ line = 273 + Will be caught by: public static void art.Test1927.test_extra_N_N_N() @ line = 204 +Caught exception: art.Test1927$TestException: from native diff --git a/test/1927-exception-event/info.txt b/test/1927-exception-event/info.txt new file mode 100644 index 0000000000..a74167fe15 --- /dev/null +++ b/test/1927-exception-event/info.txt @@ -0,0 +1,3 @@ +Test basic JVMTI exception event functionality + +Ensures that we can receive exception and exception catch events from JVMTI. diff --git a/test/1927-exception-event/run b/test/1927-exception-event/run new file mode 100755 index 0000000000..51875a7e86 --- /dev/null +++ b/test/1927-exception-event/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/1927-exception-event/src/Main.java b/test/1927-exception-event/src/Main.java new file mode 100644 index 0000000000..5b9b31b847 --- /dev/null +++ b/test/1927-exception-event/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.Test1927.run(); + } +} diff --git a/test/1927-exception-event/src/art/Breakpoint.java b/test/1927-exception-event/src/art/Breakpoint.java new file mode 100644 index 0000000000..bbb89f707f --- /dev/null +++ b/test/1927-exception-event/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/1927-exception-event/src/art/Exceptions.java b/test/1927-exception-event/src/art/Exceptions.java new file mode 100644 index 0000000000..2c959ec83f --- /dev/null +++ b/test/1927-exception-event/src/art/Exceptions.java @@ -0,0 +1,33 @@ +/* + * 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 Exceptions { + public static native void setupExceptionTracing( + Class<?> methodClass, + Class<?> exceptionClass, + Method exceptionEventMethod, + Method exceptionCaughtEventMethod); + + public static native void enableExceptionCatchEvent(Thread thr); + public static native void enableExceptionEvent(Thread thr); + public static native void disableExceptionCatchEvent(Thread thr); + public static native void disableExceptionEvent(Thread thr); +} diff --git a/test/1927-exception-event/src/art/StackTrace.java b/test/1927-exception-event/src/art/StackTrace.java new file mode 100644 index 0000000000..b12c3df66b --- /dev/null +++ b/test/1927-exception-event/src/art/StackTrace.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +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()); + if (suspend_thread) { + Suspension.suspend(thr); + } + StackFrameData[] out = nativeGetStackTrace(thr); + if (suspend_thread) { + Suspension.resume(thr); + } + return out; + } +} + diff --git a/test/1927-exception-event/src/art/Suspension.java b/test/1927-exception-event/src/art/Suspension.java new file mode 100644 index 0000000000..16e62ccac9 --- /dev/null +++ b/test/1927-exception-event/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/1927-exception-event/src/art/Test1927.java b/test/1927-exception-event/src/art/Test1927.java new file mode 100644 index 0000000000..c2d13bb4ee --- /dev/null +++ b/test/1927-exception-event/src/art/Test1927.java @@ -0,0 +1,277 @@ +/* + * 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.Objects; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; + +public class Test1927 { + private static boolean PRINT_FULL_EXCEPTION = false; + private static void PrintStack() { + System.out.println("\tCurrent Stack:"); + for (StackTrace.StackFrameData e : StackTrace.GetStackTrace(Thread.currentThread())) { + if (Objects.equals(e.method.getDeclaringClass().getPackage(), Test1927.class.getPackage())) { + System.out.println("\t\t" + e.method + " @ line = " + + Breakpoint.locationToLine(e.method, e.current_location)); + } + } + } + + public static void ExceptionCatchEvent(Thread thr, + Executable catch_method, + long catch_location, + Throwable exception) { + System.out.println(thr.getName() + ": " + catch_method + " @ line = " + + Breakpoint.locationToLine(catch_method, catch_location) + " caught " + + exception.getClass() + ": " + exception.getMessage()); + PrintStack(); + if (PRINT_FULL_EXCEPTION) { + System.out.print("exception is: "); + exception.printStackTrace(System.out); + } + } + + public static void ExceptionEvent(Thread thr, + Executable throw_method, + long throw_location, + Throwable exception, + Executable catch_method, + long catch_location) { + System.out.println(thr.getName() + ": " + throw_method + " @ line = " + + Breakpoint.locationToLine(throw_method, throw_location) + " throws " + + exception.getClass() + ": " + exception.getMessage()); + String catch_message; + if (catch_method == null) { + catch_message = "<UNKNOWN>"; + } else { + catch_message = catch_method.toString() + " @ line = " + + Breakpoint.locationToLine(catch_method, catch_location); + } + PrintStack(); + System.out.println("\tWill be caught by: " + catch_message); + if (PRINT_FULL_EXCEPTION) { + System.out.print("exception is: "); + exception.printStackTrace(System.out); + } + } + + public static class TestException extends Error { + public TestException(String s) { super(s); } + public TestException() { super("from java"); } + } + + // Possibilities + // ( -> is a JNI/Java call.) + // Furthest left catches/clears the exception + // Furthest right throws it. + // J + // N + // J -> J + // J -> N + // N -> J + // N -> N + // J -> J -> J + // J -> J -> N + // J -> N -> J + // J -> N -> N + // N -> J -> J + // N -> J -> N + // N -> N -> J + // N -> N -> N + // extra -> N -> J -> J + // extra -> N -> J -> N + // extra -> N -> N -> J + // extra -> N -> N -> N + + public static void terminal_J() { + throw new TestException(); + } + + public static native void terminal_N(); + + public static void test_J() { + try { + throw new TestException(); + } catch (TestException e) { + printException(e); + } + } + + // Do test_J but native + public static native void test_N(); + + public static void test_J_J() { + try { + terminal_J(); + } catch (TestException e) { + printException(e); + } + } + + public static void test_J_N() { + try { + terminal_N(); + } catch (TestException e) { + printException(e); + } + } + + public static native void test_N_J(); + public static native void test_N_N(); + + public static void intermediate_J_J() { terminal_J(); } + public static void intermediate_J_N() { terminal_N(); } + public static native void intermediate_N_J(); + public static native void intermediate_N_N(); + + public static void test_J_J_J() { + try { + intermediate_J_J(); + } catch (TestException e) { + printException(e); + } + } + + public static void test_J_J_N() { + try { + intermediate_J_N(); + } catch (TestException e) { + printException(e); + } + } + + public static void test_J_N_J() { + try { + intermediate_N_J(); + } catch (TestException e) { + printException(e); + } + } + + public static void test_J_N_N() { + try { + intermediate_N_N(); + } catch (TestException e) { + printException(e); + } + } + + public static native void test_N_J_J(); + public static native void test_N_J_N(); + public static native void test_N_N_J(); + public static native void test_N_N_N(); + + public static void test_extra_N_J_J() { + try { + test_N_J_J(); + } catch (TestException e) { + printException(e); + } + } + public static void test_extra_N_J_N() { + try { + test_N_J_N(); + } catch (TestException e) { + printException(e); + } + } + public static void test_extra_N_N_J() { + try { + test_N_N_J(); + } catch (TestException e) { + printException(e); + } + } + public static void test_extra_N_N_N() { + try { + test_N_N_N(); + } catch (TestException e) { + printException(e); + } + } + + public static void printException(Throwable e) { + System.out.println("Caught exception: " + e); + if (PRINT_FULL_EXCEPTION) { + e.printStackTrace(System.out); + } + } + + public static void run() throws Exception { + // Make sure classes are loaded first. + System.out.println(TestException.class.toString()); + Exceptions.setupExceptionTracing( + Test1927.class, + TestException.class, + Test1927.class.getDeclaredMethod( + "ExceptionEvent", + Thread.class, + Executable.class, + Long.TYPE, + Throwable.class, + Executable.class, + Long.TYPE), + Test1927.class.getDeclaredMethod( + "ExceptionCatchEvent", + Thread.class, + Executable.class, + Long.TYPE, + Throwable.class)); + Exceptions.enableExceptionEvent(Thread.currentThread()); + Exceptions.enableExceptionCatchEvent(Thread.currentThread()); + System.out.println("Running test_J"); + test_J(); + System.out.println("Running test_N()"); + test_N(); + System.out.println("Running test_J_J()"); + test_J_J(); + System.out.println("Running test_J_N()"); + test_J_N(); + System.out.println("Running test_N_J()"); + test_N_J(); + System.out.println("Running test_N_N()"); + test_N_N(); + System.out.println("Running test_J_J_J()"); + test_J_J_J(); + System.out.println("Running test_J_J_N()"); + test_J_J_N(); + System.out.println("Running test_J_N_J()"); + test_J_N_J(); + System.out.println("Running test_J_N_N()"); + test_J_N_N(); + System.out.println("Running test_N_J_J()"); + test_N_J_J(); + System.out.println("Running test_N_J_N()"); + test_N_J_N(); + System.out.println("Running test_N_N_J()"); + test_N_N_J(); + System.out.println("Running test_N_N_N()"); + test_N_N_N(); + System.out.println("Running test_extra_N_J_J()"); + test_extra_N_J_J(); + System.out.println("Running test_extra_N_J_N()"); + test_extra_N_J_N(); + System.out.println("Running test_extra_N_N_J()"); + test_extra_N_N_J(); + System.out.println("Running test_extra_N_N_N()"); + test_extra_N_N_N(); + Exceptions.disableExceptionCatchEvent(Thread.currentThread()); + Exceptions.disableExceptionEvent(Thread.currentThread()); + } +} diff --git a/test/1928-exception-event-exception/expected.txt b/test/1928-exception-event-exception/expected.txt new file mode 100644 index 0000000000..1692d042e4 --- /dev/null +++ b/test/1928-exception-event-exception/expected.txt @@ -0,0 +1,236 @@ +Test "art.Test1928$DoThrowClass": Running with handler "art.Test1928$DoNothingHandler" +main: public static void art.Test1928.doThrow() @ line = 110 throws class art.Test1928$TestException: doThrow + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1928.PrintStack() @ line = 35 + public static void art.Test1928.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 59 + public static void art.Test1928.doThrow() @ line = 110 + public void art.Test1928$DoThrowClass.run() @ line = 114 + public static void art.Test1928.run() throws java.lang.Exception @ line = 196 + Will be caught by: public static void art.Test1928.run() throws java.lang.Exception @ line = 199 +exception is: art.Test1928$TestException: doThrow + at art.Test1928.doThrow(Test1928.java:110) + at art.Test1928$DoThrowClass.run(Test1928.java:114) + at art.Test1928.run(Test1928.java:196) + at Main.main(Main.java:19) + Doing nothing! +Test "art.Test1928$DoThrowClass": Caught error art.Test1928$TestException:"doThrow" with handler "art.Test1928$DoNothingHandler" +art.Test1928$TestException: doThrow + at art.Test1928.doThrow(Test1928.java:110) + at art.Test1928$DoThrowClass.run(Test1928.java:114) + at art.Test1928.run(Test1928.java:196) + at Main.main(Main.java:19) +Test "art.Test1928$DoThrowClass": Finished running with handler "art.Test1928$DoNothingHandler" +Test "art.Test1928$DoThrowCatchBaseTestException": Running with handler "art.Test1928$DoNothingHandler" +main: public static void art.Test1928.throwCatchBaseTestException() @ line = 119 throws class art.Test1928$TestException: throwCatchBaseTestException + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1928.PrintStack() @ line = 35 + public static void art.Test1928.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 59 + public static void art.Test1928.throwCatchBaseTestException() @ line = 119 + public void art.Test1928$DoThrowCatchBaseTestException.run() @ line = 129 + public static void art.Test1928.run() throws java.lang.Exception @ line = 196 + Will be caught by: public static void art.Test1928.throwCatchBaseTestException() @ line = 120 +exception is: art.Test1928$TestException: throwCatchBaseTestException + at art.Test1928.throwCatchBaseTestException(Test1928.java:119) + at art.Test1928$DoThrowCatchBaseTestException.run(Test1928.java:129) + at art.Test1928.run(Test1928.java:196) + at Main.main(Main.java:19) + Doing nothing! +Caught art.Test1928$TestException: "throwCatchBaseTestException" +art.Test1928$TestException: throwCatchBaseTestException + at art.Test1928.throwCatchBaseTestException(Test1928.java:119) + at art.Test1928$DoThrowCatchBaseTestException.run(Test1928.java:129) + at art.Test1928.run(Test1928.java:196) + at Main.main(Main.java:19) +Test "art.Test1928$DoThrowCatchBaseTestException": No error caught with handler "art.Test1928$DoNothingHandler" +Test "art.Test1928$DoThrowCatchBaseTestException": Finished running with handler "art.Test1928$DoNothingHandler" +Test "art.Test1928$DoThrowCatchTestException": Running with handler "art.Test1928$DoNothingHandler" +main: public static void art.Test1928.throwCatchTestException() @ line = 134 throws class art.Test1928$TestException: throwCatchTestException + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1928.PrintStack() @ line = 35 + public static void art.Test1928.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 59 + public static void art.Test1928.throwCatchTestException() @ line = 134 + public void art.Test1928$DoThrowCatchTestException.run() @ line = 144 + public static void art.Test1928.run() throws java.lang.Exception @ line = 196 + Will be caught by: public static void art.Test1928.throwCatchTestException() @ line = 135 +exception is: art.Test1928$TestException: throwCatchTestException + at art.Test1928.throwCatchTestException(Test1928.java:134) + at art.Test1928$DoThrowCatchTestException.run(Test1928.java:144) + at art.Test1928.run(Test1928.java:196) + at Main.main(Main.java:19) + Doing nothing! +Caught art.Test1928$TestException: "throwCatchTestException" +art.Test1928$TestException: throwCatchTestException + at art.Test1928.throwCatchTestException(Test1928.java:134) + at art.Test1928$DoThrowCatchTestException.run(Test1928.java:144) + at art.Test1928.run(Test1928.java:196) + at Main.main(Main.java:19) +Test "art.Test1928$DoThrowCatchTestException": No error caught with handler "art.Test1928$DoNothingHandler" +Test "art.Test1928$DoThrowCatchTestException": Finished running with handler "art.Test1928$DoNothingHandler" +Test "art.Test1928$DoThrowCatchTestExceptionNoRethrow": Running with handler "art.Test1928$DoNothingHandler" +main: public static void art.Test1928.throwCatchTestExceptionNoRethrow() @ line = 149 throws class art.Test1928$TestException: throwCatchTestExceptionNoRethrow + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1928.PrintStack() @ line = 35 + public static void art.Test1928.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 59 + public static void art.Test1928.throwCatchTestExceptionNoRethrow() @ line = 149 + public void art.Test1928$DoThrowCatchTestExceptionNoRethrow.run() @ line = 159 + public static void art.Test1928.run() throws java.lang.Exception @ line = 196 + Will be caught by: public static void art.Test1928.run() throws java.lang.Exception @ line = 199 +exception is: art.Test1928$TestException: throwCatchTestExceptionNoRethrow + at art.Test1928.throwCatchTestExceptionNoRethrow(Test1928.java:149) + at art.Test1928$DoThrowCatchTestExceptionNoRethrow.run(Test1928.java:159) + at art.Test1928.run(Test1928.java:196) + at Main.main(Main.java:19) + Doing nothing! +Test "art.Test1928$DoThrowCatchTestExceptionNoRethrow": Caught error art.Test1928$TestException:"throwCatchTestExceptionNoRethrow" with handler "art.Test1928$DoNothingHandler" +art.Test1928$TestException: throwCatchTestExceptionNoRethrow + at art.Test1928.throwCatchTestExceptionNoRethrow(Test1928.java:149) + at art.Test1928$DoThrowCatchTestExceptionNoRethrow.run(Test1928.java:159) + at art.Test1928.run(Test1928.java:196) + at Main.main(Main.java:19) +Test "art.Test1928$DoThrowCatchTestExceptionNoRethrow": Finished running with handler "art.Test1928$DoNothingHandler" +Test "art.Test1928$DoThrowClass": Running with handler "art.Test1928$ThrowCatchBase" +main: public static void art.Test1928.doThrow() @ line = 110 throws class art.Test1928$TestException: doThrow + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1928.PrintStack() @ line = 35 + public static void art.Test1928.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 59 + public static void art.Test1928.doThrow() @ line = 110 + public void art.Test1928$DoThrowClass.run() @ line = 114 + public static void art.Test1928.run() throws java.lang.Exception @ line = 196 + Will be caught by: public static void art.Test1928.run() throws java.lang.Exception @ line = 199 +exception is: art.Test1928$TestException: doThrow + at art.Test1928.doThrow(Test1928.java:110) + at art.Test1928$DoThrowClass.run(Test1928.java:114) + at art.Test1928.run(Test1928.java:196) + at Main.main(Main.java:19) + Throwing BaseTestException and catching it! +Caught art.Test1928$BaseTestException: "ThrowBaseHandler during throw from public static void art.Test1928.doThrow() @ line = 110" +art.Test1928$BaseTestException: ThrowBaseHandler during throw from public static void art.Test1928.doThrow() @ line = 110 + at art.Test1928$ThrowCatchBase.exceptionOccurred(Test1928.java:99) + at art.Test1928.ExceptionEvent(Test1928.java:66) + at art.Test1928.doThrow(Test1928.java:110) + at art.Test1928$DoThrowClass.run(Test1928.java:114) + at art.Test1928.run(Test1928.java:196) + at Main.main(Main.java:19) +Caused by: art.Test1928$TestException: doThrow + ... 4 more +Test "art.Test1928$DoThrowClass": Caught error art.Test1928$TestException:"doThrow" with handler "art.Test1928$ThrowCatchBase" +art.Test1928$TestException: doThrow + at art.Test1928.doThrow(Test1928.java:110) + at art.Test1928$DoThrowClass.run(Test1928.java:114) + at art.Test1928.run(Test1928.java:196) + at Main.main(Main.java:19) +Test "art.Test1928$DoThrowClass": Finished running with handler "art.Test1928$ThrowCatchBase" +Test "art.Test1928$DoThrowCatchBaseTestException": Running with handler "art.Test1928$ThrowCatchBase" +main: public static void art.Test1928.throwCatchBaseTestException() @ line = 119 throws class art.Test1928$TestException: throwCatchBaseTestException + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1928.PrintStack() @ line = 35 + public static void art.Test1928.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 59 + public static void art.Test1928.throwCatchBaseTestException() @ line = 119 + public void art.Test1928$DoThrowCatchBaseTestException.run() @ line = 129 + public static void art.Test1928.run() throws java.lang.Exception @ line = 196 + Will be caught by: public static void art.Test1928.throwCatchBaseTestException() @ line = 120 +exception is: art.Test1928$TestException: throwCatchBaseTestException + at art.Test1928.throwCatchBaseTestException(Test1928.java:119) + at art.Test1928$DoThrowCatchBaseTestException.run(Test1928.java:129) + at art.Test1928.run(Test1928.java:196) + at Main.main(Main.java:19) + Throwing BaseTestException and catching it! +Caught art.Test1928$BaseTestException: "ThrowBaseHandler during throw from public static void art.Test1928.throwCatchBaseTestException() @ line = 119" +art.Test1928$BaseTestException: ThrowBaseHandler during throw from public static void art.Test1928.throwCatchBaseTestException() @ line = 119 + at art.Test1928$ThrowCatchBase.exceptionOccurred(Test1928.java:99) + at art.Test1928.ExceptionEvent(Test1928.java:66) + at art.Test1928.throwCatchBaseTestException(Test1928.java:119) + at art.Test1928$DoThrowCatchBaseTestException.run(Test1928.java:129) + at art.Test1928.run(Test1928.java:196) + at Main.main(Main.java:19) +Caused by: art.Test1928$TestException: throwCatchBaseTestException + ... 4 more +Caught art.Test1928$TestException: "throwCatchBaseTestException" +art.Test1928$TestException: throwCatchBaseTestException + at art.Test1928.throwCatchBaseTestException(Test1928.java:119) + at art.Test1928$DoThrowCatchBaseTestException.run(Test1928.java:129) + at art.Test1928.run(Test1928.java:196) + at Main.main(Main.java:19) +Test "art.Test1928$DoThrowCatchBaseTestException": No error caught with handler "art.Test1928$ThrowCatchBase" +Test "art.Test1928$DoThrowCatchBaseTestException": Finished running with handler "art.Test1928$ThrowCatchBase" +Test "art.Test1928$DoThrowCatchTestException": Running with handler "art.Test1928$ThrowCatchBase" +main: public static void art.Test1928.throwCatchTestException() @ line = 134 throws class art.Test1928$TestException: throwCatchTestException + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1928.PrintStack() @ line = 35 + public static void art.Test1928.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 59 + public static void art.Test1928.throwCatchTestException() @ line = 134 + public void art.Test1928$DoThrowCatchTestException.run() @ line = 144 + public static void art.Test1928.run() throws java.lang.Exception @ line = 196 + Will be caught by: public static void art.Test1928.throwCatchTestException() @ line = 135 +exception is: art.Test1928$TestException: throwCatchTestException + at art.Test1928.throwCatchTestException(Test1928.java:134) + at art.Test1928$DoThrowCatchTestException.run(Test1928.java:144) + at art.Test1928.run(Test1928.java:196) + at Main.main(Main.java:19) + Throwing BaseTestException and catching it! +Caught art.Test1928$BaseTestException: "ThrowBaseHandler during throw from public static void art.Test1928.throwCatchTestException() @ line = 134" +art.Test1928$BaseTestException: ThrowBaseHandler during throw from public static void art.Test1928.throwCatchTestException() @ line = 134 + at art.Test1928$ThrowCatchBase.exceptionOccurred(Test1928.java:99) + at art.Test1928.ExceptionEvent(Test1928.java:66) + at art.Test1928.throwCatchTestException(Test1928.java:134) + at art.Test1928$DoThrowCatchTestException.run(Test1928.java:144) + at art.Test1928.run(Test1928.java:196) + at Main.main(Main.java:19) +Caused by: art.Test1928$TestException: throwCatchTestException + ... 4 more +Caught art.Test1928$TestException: "throwCatchTestException" +art.Test1928$TestException: throwCatchTestException + at art.Test1928.throwCatchTestException(Test1928.java:134) + at art.Test1928$DoThrowCatchTestException.run(Test1928.java:144) + at art.Test1928.run(Test1928.java:196) + at Main.main(Main.java:19) +Test "art.Test1928$DoThrowCatchTestException": No error caught with handler "art.Test1928$ThrowCatchBase" +Test "art.Test1928$DoThrowCatchTestException": Finished running with handler "art.Test1928$ThrowCatchBase" +Test "art.Test1928$DoThrowCatchTestExceptionNoRethrow": Running with handler "art.Test1928$ThrowCatchBase" +main: public static void art.Test1928.throwCatchTestExceptionNoRethrow() @ line = 149 throws class art.Test1928$TestException: throwCatchTestExceptionNoRethrow + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1928.PrintStack() @ line = 35 + public static void art.Test1928.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 59 + public static void art.Test1928.throwCatchTestExceptionNoRethrow() @ line = 149 + public void art.Test1928$DoThrowCatchTestExceptionNoRethrow.run() @ line = 159 + public static void art.Test1928.run() throws java.lang.Exception @ line = 196 + Will be caught by: public static void art.Test1928.run() throws java.lang.Exception @ line = 199 +exception is: art.Test1928$TestException: throwCatchTestExceptionNoRethrow + at art.Test1928.throwCatchTestExceptionNoRethrow(Test1928.java:149) + at art.Test1928$DoThrowCatchTestExceptionNoRethrow.run(Test1928.java:159) + at art.Test1928.run(Test1928.java:196) + at Main.main(Main.java:19) + Throwing BaseTestException and catching it! +Caught art.Test1928$BaseTestException: "ThrowBaseHandler during throw from public static void art.Test1928.throwCatchTestExceptionNoRethrow() @ line = 149" +art.Test1928$BaseTestException: ThrowBaseHandler during throw from public static void art.Test1928.throwCatchTestExceptionNoRethrow() @ line = 149 + at art.Test1928$ThrowCatchBase.exceptionOccurred(Test1928.java:99) + at art.Test1928.ExceptionEvent(Test1928.java:66) + at art.Test1928.throwCatchTestExceptionNoRethrow(Test1928.java:149) + at art.Test1928$DoThrowCatchTestExceptionNoRethrow.run(Test1928.java:159) + at art.Test1928.run(Test1928.java:196) + at Main.main(Main.java:19) +Caused by: art.Test1928$TestException: throwCatchTestExceptionNoRethrow + ... 4 more +Test "art.Test1928$DoThrowCatchTestExceptionNoRethrow": Caught error art.Test1928$TestException:"throwCatchTestExceptionNoRethrow" with handler "art.Test1928$ThrowCatchBase" +art.Test1928$TestException: throwCatchTestExceptionNoRethrow + at art.Test1928.throwCatchTestExceptionNoRethrow(Test1928.java:149) + at art.Test1928$DoThrowCatchTestExceptionNoRethrow.run(Test1928.java:159) + at art.Test1928.run(Test1928.java:196) + at Main.main(Main.java:19) +Test "art.Test1928$DoThrowCatchTestExceptionNoRethrow": Finished running with handler "art.Test1928$ThrowCatchBase" diff --git a/test/1928-exception-event-exception/info.txt b/test/1928-exception-event-exception/info.txt new file mode 100644 index 0000000000..ef8474616a --- /dev/null +++ b/test/1928-exception-event-exception/info.txt @@ -0,0 +1,5 @@ +Test basic JVMTI exception event functionality. + +Ensures we can throw exceptions during the exception event without causing +problems. Note that we do not allow exceptions to propogate past the event, +matching RI behavior. See b/65049545. diff --git a/test/1928-exception-event-exception/run b/test/1928-exception-event-exception/run new file mode 100755 index 0000000000..51875a7e86 --- /dev/null +++ b/test/1928-exception-event-exception/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/1928-exception-event-exception/src/Main.java b/test/1928-exception-event-exception/src/Main.java new file mode 100644 index 0000000000..11cc773041 --- /dev/null +++ b/test/1928-exception-event-exception/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.Test1928.run(); + } +} diff --git a/test/1928-exception-event-exception/src/art/Breakpoint.java b/test/1928-exception-event-exception/src/art/Breakpoint.java new file mode 100644 index 0000000000..bbb89f707f --- /dev/null +++ b/test/1928-exception-event-exception/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/1928-exception-event-exception/src/art/Exceptions.java b/test/1928-exception-event-exception/src/art/Exceptions.java new file mode 100644 index 0000000000..2c959ec83f --- /dev/null +++ b/test/1928-exception-event-exception/src/art/Exceptions.java @@ -0,0 +1,33 @@ +/* + * 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 Exceptions { + public static native void setupExceptionTracing( + Class<?> methodClass, + Class<?> exceptionClass, + Method exceptionEventMethod, + Method exceptionCaughtEventMethod); + + public static native void enableExceptionCatchEvent(Thread thr); + public static native void enableExceptionEvent(Thread thr); + public static native void disableExceptionCatchEvent(Thread thr); + public static native void disableExceptionEvent(Thread thr); +} diff --git a/test/1928-exception-event-exception/src/art/StackTrace.java b/test/1928-exception-event-exception/src/art/StackTrace.java new file mode 100644 index 0000000000..b12c3df66b --- /dev/null +++ b/test/1928-exception-event-exception/src/art/StackTrace.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +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()); + if (suspend_thread) { + Suspension.suspend(thr); + } + StackFrameData[] out = nativeGetStackTrace(thr); + if (suspend_thread) { + Suspension.resume(thr); + } + return out; + } +} + diff --git a/test/1928-exception-event-exception/src/art/Suspension.java b/test/1928-exception-event-exception/src/art/Suspension.java new file mode 100644 index 0000000000..16e62ccac9 --- /dev/null +++ b/test/1928-exception-event-exception/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/1928-exception-event-exception/src/art/Test1928.java b/test/1928-exception-event-exception/src/art/Test1928.java new file mode 100644 index 0000000000..aec88a4079 --- /dev/null +++ b/test/1928-exception-event-exception/src/art/Test1928.java @@ -0,0 +1,216 @@ +/* + * 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.Objects; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; + +public class Test1928 { + public static boolean PRINT_FULL_EXCEPTION = true; + public static ExceptionHandler HANDLER = null; + + public static interface ExceptionHandler { + public void exceptionOccurred( + Executable m, long loc, Throwable exception, Executable catch_m, long catch_l); + } + + private static void PrintStack() { + System.out.println("\tCurrent Stack:"); + for (StackTrace.StackFrameData e : StackTrace.GetStackTrace(Thread.currentThread())) { + if (Objects.equals(e.method.getDeclaringClass().getPackage(), Test1928.class.getPackage())) { + System.out.println("\t\t" + e.method + " @ line = " + + Breakpoint.locationToLine(e.method, e.current_location)); + } + } + } + + public static void ExceptionEvent(Thread thr, + Executable throw_method, + long throw_location, + Throwable exception, + Executable catch_method, + long catch_location) { + System.out.println(thr.getName() + ": " + throw_method + " @ line = " + + Breakpoint.locationToLine(throw_method, throw_location) + " throws " + + exception.getClass() + ": " + exception.getMessage()); + String catch_message; + if (catch_method == null) { + catch_message = "<UNKNOWN>"; + } else { + catch_message = catch_method.toString() + " @ line = " + + Breakpoint.locationToLine(catch_method, catch_location); + } + PrintStack(); + System.out.println("\tWill be caught by: " + catch_message); + if (PRINT_FULL_EXCEPTION) { + System.out.print("exception is: "); + exception.printStackTrace(System.out); + } + if (HANDLER != null) { + HANDLER.exceptionOccurred( + throw_method, throw_location, exception, catch_method, catch_location); + } + } + + public static class BaseTestException extends Error { + public BaseTestException(String e) { super(e); } + public BaseTestException(String e, Throwable t) { super(e, t); } + } + public static class TestException extends BaseTestException { + public TestException(String e) { super(e); } + public TestException(String e, Throwable t) { super(e, t); } + } + + public static class TestExceptionNoRethrow extends TestException { + public TestExceptionNoRethrow(String e) { super(e); } + public TestExceptionNoRethrow(String e, Throwable t) { super(e, t); } + } + + public static class DoNothingHandler implements ExceptionHandler { + public void exceptionOccurred( + Executable m, long loc, Throwable exception, Executable catch_m, long catch_l) { + System.out.println("\tDoing nothing!"); + return; + } + } + + public static class ThrowCatchBase implements ExceptionHandler { + public void exceptionOccurred( + Executable m, long loc, Throwable exception, Executable catch_m, long catch_l) { + System.out.println("\tThrowing BaseTestException and catching it!"); + try { + throw new BaseTestException("ThrowBaseHandler during throw from " + m + " @ line = " + + Breakpoint.locationToLine(m, loc), exception); + } catch (BaseTestException t) { + System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); + if (PRINT_FULL_EXCEPTION) { + t.printStackTrace(System.out); + } + } + } + } + + public static void doThrow() { + throw new TestException("doThrow"); + } + + public static class DoThrowClass implements Runnable { + public void run() { doThrow(); } + } + + public static void throwCatchBaseTestException() { + try { + throw new TestException("throwCatchBaseTestException"); + } catch (BaseTestException t) { + System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); + if (PRINT_FULL_EXCEPTION) { + t.printStackTrace(System.out); + } + } + } + + public static class DoThrowCatchBaseTestException implements Runnable { + public void run() { throwCatchBaseTestException(); } + } + + public static void throwCatchTestException() { + try { + throw new TestException("throwCatchTestException"); + } catch (TestException t) { + System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); + if (PRINT_FULL_EXCEPTION) { + t.printStackTrace(System.out); + } + } + } + + public static class DoThrowCatchTestException implements Runnable { + public void run() { throwCatchTestException(); } + } + + public static void throwCatchTestExceptionNoRethrow() { + try { + throw new TestException("throwCatchTestExceptionNoRethrow"); + } catch (TestExceptionNoRethrow t) { + System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); + if (PRINT_FULL_EXCEPTION) { + t.printStackTrace(System.out); + } + } + } + + public static class DoThrowCatchTestExceptionNoRethrow implements Runnable { + public void run() { throwCatchTestExceptionNoRethrow(); } + } + + public static void run() throws Exception { + // Set up + Exceptions.setupExceptionTracing( + Test1928.class, + TestException.class, + Test1928.class.getDeclaredMethod( + "ExceptionEvent", + Thread.class, + Executable.class, + Long.TYPE, + Throwable.class, + Executable.class, + Long.TYPE), + null); + Exceptions.enableExceptionEvent(Thread.currentThread()); + + ExceptionHandler[] handlers = new ExceptionHandler[] { + new DoNothingHandler(), + new ThrowCatchBase(), + }; + + Runnable[] tests = new Runnable[] { + new DoThrowClass(), + new DoThrowCatchBaseTestException(), + new DoThrowCatchTestException(), + new DoThrowCatchTestExceptionNoRethrow(), + }; + + for (ExceptionHandler handler : handlers) { + for (Runnable test : tests) { + try { + HANDLER = handler; + System.out.printf("Test \"%s\": Running with handler \"%s\"\n", + test.getClass().getName(), handler.getClass().getName()); + test.run(); + System.out.printf("Test \"%s\": No error caught with handler \"%s\"\n", + test.getClass().getName(), handler.getClass().getName()); + } catch (Throwable e) { + System.out.printf("Test \"%s\": Caught error %s:\"%s\" with handler \"%s\"\n", + test.getClass().getName(), + e.getClass().getName(), + e.getMessage(), + handler.getClass().getName()); + if (PRINT_FULL_EXCEPTION) { + e.printStackTrace(System.out); + } + } + System.out.printf("Test \"%s\": Finished running with handler \"%s\"\n", + test.getClass().getName(), handler.getClass().getName()); + HANDLER = null; + } + } + Exceptions.disableExceptionEvent(Thread.currentThread()); + } +} diff --git a/test/1929-exception-catch-exception/expected.txt b/test/1929-exception-catch-exception/expected.txt new file mode 100644 index 0000000000..7c23a31439 --- /dev/null +++ b/test/1929-exception-catch-exception/expected.txt @@ -0,0 +1,302 @@ +Test "art.Test1929$DoThrowClass": Running breakpoint with handler "art.Test1929$DoNothingHandler" +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: doThrow + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929.run() throws java.lang.Exception @ line = 283 +Test "art.Test1929$DoThrowClass": Caught error art.Test1929$TestException:"doThrow" with handler "art.Test1929$DoNothingHandler" +Test "art.Test1929$DoThrowClass": Finished running with handler "art.Test1929$DoNothingHandler" +Test "art.Test1929$DoThrowCatchBaseTestException": Running breakpoint with handler "art.Test1929$DoNothingHandler" +main: public static void art.Test1929.throwCatchBaseTestException() @ line = 140 caught class art.Test1929$TestException: throwCatchBaseTestException + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929.throwCatchBaseTestException() @ line = 140 + public void art.Test1929$DoThrowCatchBaseTestException.run() @ line = 149 + public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + Doing nothing! +Caught art.Test1929$TestException: "throwCatchBaseTestException" +Test "art.Test1929$DoThrowCatchBaseTestException": No error caught with handler "art.Test1929$DoNothingHandler" +Test "art.Test1929$DoThrowCatchBaseTestException": Finished running with handler "art.Test1929$DoNothingHandler" +Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Running breakpoint with handler "art.Test1929$DoNothingHandler" +main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 + public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 197 + public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 201 + public static void art.Test1929.run() throws java.lang.Exception @ line = 280 +Caught art.Test1929$TestException: "throwCatchBaseTestExceptionTwice" +Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": No error caught with handler "art.Test1929$DoNothingHandler" +Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Finished running with handler "art.Test1929$DoNothingHandler" +Test "art.Test1929$DoThrowCatchTestException": Running breakpoint with handler "art.Test1929$DoNothingHandler" +main: public static void art.Test1929.throwCatchTestException() @ line = 207 caught class art.Test1929$TestException: throwCatchTestException + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929.throwCatchTestException() @ line = 207 + public void art.Test1929$DoThrowCatchTestException.run() @ line = 216 + public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + Doing nothing! +Caught art.Test1929$TestException: "throwCatchTestException" +Test "art.Test1929$DoThrowCatchTestException": No error caught with handler "art.Test1929$DoNothingHandler" +Test "art.Test1929$DoThrowCatchTestException": Finished running with handler "art.Test1929$DoNothingHandler" +Test "art.Test1929$DoThrowCatchTestExceptionTwice": Running breakpoint with handler "art.Test1929$DoNothingHandler" +main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 caught class art.Test1929$TestException: throwCatchTestExceptionTwice + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 + public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 222 + public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 226 + public static void art.Test1929.run() throws java.lang.Exception @ line = 280 +Caught art.Test1929$TestException: "throwCatchTestExceptionTwice" +Test "art.Test1929$DoThrowCatchTestExceptionTwice": No error caught with handler "art.Test1929$DoNothingHandler" +Test "art.Test1929$DoThrowCatchTestExceptionTwice": Finished running with handler "art.Test1929$DoNothingHandler" +Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Running breakpoint with handler "art.Test1929$DoNothingHandler" +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929.run() throws java.lang.Exception @ line = 283 +Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Caught error art.Test1929$TestException:"throwCatchTestExceptionNoRethrow" with handler "art.Test1929$DoNothingHandler" +Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Finished running with handler "art.Test1929$DoNothingHandler" +Test "art.Test1929$DoThrowClass": Running breakpoint with handler "art.Test1929$ThrowCatchBase" +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: doThrow + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929.run() throws java.lang.Exception @ line = 283 +Test "art.Test1929$DoThrowClass": Caught error art.Test1929$TestException:"doThrow" with handler "art.Test1929$ThrowCatchBase" +Test "art.Test1929$DoThrowClass": Finished running with handler "art.Test1929$ThrowCatchBase" +Test "art.Test1929$DoThrowCatchBaseTestException": Running breakpoint with handler "art.Test1929$ThrowCatchBase" +main: public static void art.Test1929.throwCatchBaseTestException() @ line = 140 caught class art.Test1929$TestException: throwCatchBaseTestException + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929.throwCatchBaseTestException() @ line = 140 + public void art.Test1929$DoThrowCatchBaseTestException.run() @ line = 149 + public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + Throwing BaseTestException and catching it! +Caught art.Test1929$BaseTestException: "ThrowBaseHandler during throw from public static void art.Test1929.throwCatchBaseTestException() @ line = 140" +Caught art.Test1929$TestException: "throwCatchBaseTestException" +Test "art.Test1929$DoThrowCatchBaseTestException": No error caught with handler "art.Test1929$ThrowCatchBase" +Test "art.Test1929$DoThrowCatchBaseTestException": Finished running with handler "art.Test1929$ThrowCatchBase" +Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowCatchBase" +main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 + public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 197 + public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 201 + public static void art.Test1929.run() throws java.lang.Exception @ line = 280 +Caught art.Test1929$TestException: "throwCatchBaseTestExceptionTwice" +Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": No error caught with handler "art.Test1929$ThrowCatchBase" +Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Finished running with handler "art.Test1929$ThrowCatchBase" +Test "art.Test1929$DoThrowCatchTestException": Running breakpoint with handler "art.Test1929$ThrowCatchBase" +main: public static void art.Test1929.throwCatchTestException() @ line = 207 caught class art.Test1929$TestException: throwCatchTestException + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929.throwCatchTestException() @ line = 207 + public void art.Test1929$DoThrowCatchTestException.run() @ line = 216 + public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + Throwing BaseTestException and catching it! +Caught art.Test1929$BaseTestException: "ThrowBaseHandler during throw from public static void art.Test1929.throwCatchTestException() @ line = 207" +Caught art.Test1929$TestException: "throwCatchTestException" +Test "art.Test1929$DoThrowCatchTestException": No error caught with handler "art.Test1929$ThrowCatchBase" +Test "art.Test1929$DoThrowCatchTestException": Finished running with handler "art.Test1929$ThrowCatchBase" +Test "art.Test1929$DoThrowCatchTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowCatchBase" +main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 caught class art.Test1929$TestException: throwCatchTestExceptionTwice + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 + public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 222 + public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 226 + public static void art.Test1929.run() throws java.lang.Exception @ line = 280 +Caught art.Test1929$TestException: "throwCatchTestExceptionTwice" +Test "art.Test1929$DoThrowCatchTestExceptionTwice": No error caught with handler "art.Test1929$ThrowCatchBase" +Test "art.Test1929$DoThrowCatchTestExceptionTwice": Finished running with handler "art.Test1929$ThrowCatchBase" +Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Running breakpoint with handler "art.Test1929$ThrowCatchBase" +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929.run() throws java.lang.Exception @ line = 283 +Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Caught error art.Test1929$TestException:"throwCatchTestExceptionNoRethrow" with handler "art.Test1929$ThrowCatchBase" +Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Finished running with handler "art.Test1929$ThrowCatchBase" +Test "art.Test1929$DoThrowClass": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler" +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: doThrow + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929.run() throws java.lang.Exception @ line = 283 +Test "art.Test1929$DoThrowClass": Caught error art.Test1929$TestException:"doThrow" with handler "art.Test1929$ThrowBaseTestExceptionHandler" +Test "art.Test1929$DoThrowClass": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler" +Test "art.Test1929$DoThrowCatchBaseTestException": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler" +main: public static void art.Test1929.throwCatchBaseTestException() @ line = 140 caught class art.Test1929$TestException: throwCatchBaseTestException + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929.throwCatchBaseTestException() @ line = 140 + public void art.Test1929$DoThrowCatchBaseTestException.run() @ line = 149 + public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + Throwing BaseTestException! +Test "art.Test1929$DoThrowCatchBaseTestException": Caught error art.Test1929$BaseTestException:"ThrowBaseHandler during throw from public static void art.Test1929.throwCatchBaseTestException() @ line = 140" with handler "art.Test1929$ThrowBaseTestExceptionHandler" +Test "art.Test1929$DoThrowCatchBaseTestException": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler" +Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler" +main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 + public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 197 + public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 201 + public static void art.Test1929.run() throws java.lang.Exception @ line = 280 +Caught art.Test1929$TestException: "throwCatchBaseTestExceptionTwice" +Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": No error caught with handler "art.Test1929$ThrowBaseTestExceptionHandler" +Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler" +Test "art.Test1929$DoThrowCatchTestException": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler" +main: public static void art.Test1929.throwCatchTestException() @ line = 207 caught class art.Test1929$TestException: throwCatchTestException + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929.throwCatchTestException() @ line = 207 + public void art.Test1929$DoThrowCatchTestException.run() @ line = 216 + public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + Throwing BaseTestException! +Test "art.Test1929$DoThrowCatchTestException": Caught error art.Test1929$BaseTestException:"ThrowBaseHandler during throw from public static void art.Test1929.throwCatchTestException() @ line = 207" with handler "art.Test1929$ThrowBaseTestExceptionHandler" +Test "art.Test1929$DoThrowCatchTestException": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler" +Test "art.Test1929$DoThrowCatchTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler" +main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 caught class art.Test1929$TestException: throwCatchTestExceptionTwice + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 + public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 222 + public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 226 + public static void art.Test1929.run() throws java.lang.Exception @ line = 280 +Caught art.Test1929$TestException: "throwCatchTestExceptionTwice" +Test "art.Test1929$DoThrowCatchTestExceptionTwice": No error caught with handler "art.Test1929$ThrowBaseTestExceptionHandler" +Test "art.Test1929$DoThrowCatchTestExceptionTwice": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler" +Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler" +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929.run() throws java.lang.Exception @ line = 283 +Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Caught error art.Test1929$TestException:"throwCatchTestExceptionNoRethrow" with handler "art.Test1929$ThrowBaseTestExceptionHandler" +Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler" +Test "art.Test1929$DoThrowClass": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: doThrow + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929.run() throws java.lang.Exception @ line = 283 +Test "art.Test1929$DoThrowClass": Caught error art.Test1929$TestException:"doThrow" with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" +Test "art.Test1929$DoThrowClass": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" +Test "art.Test1929$DoThrowCatchBaseTestException": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" +main: public static void art.Test1929.throwCatchBaseTestException() @ line = 140 caught class art.Test1929$TestException: throwCatchBaseTestException + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929.throwCatchBaseTestException() @ line = 140 + public void art.Test1929$DoThrowCatchBaseTestException.run() @ line = 149 + public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + Throwing TestExceptionNoRethrow! +Test "art.Test1929$DoThrowCatchBaseTestException": Caught error art.Test1929$TestExceptionNoRethrow:"ThrowTestExceptionNoRethrowHandler during throw from public static void art.Test1929.throwCatchBaseTestException() @ line = 140" with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" +Test "art.Test1929$DoThrowCatchBaseTestException": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" +Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" +main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 + public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 197 + public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 201 + public static void art.Test1929.run() throws java.lang.Exception @ line = 280 +Caught art.Test1929$TestException: "throwCatchBaseTestExceptionTwice" +Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": No error caught with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" +Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" +Test "art.Test1929$DoThrowCatchTestException": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" +main: public static void art.Test1929.throwCatchTestException() @ line = 207 caught class art.Test1929$TestException: throwCatchTestException + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929.throwCatchTestException() @ line = 207 + public void art.Test1929$DoThrowCatchTestException.run() @ line = 216 + public static void art.Test1929.run() throws java.lang.Exception @ line = 280 + Throwing TestExceptionNoRethrow! +Test "art.Test1929$DoThrowCatchTestException": Caught error art.Test1929$TestExceptionNoRethrow:"ThrowTestExceptionNoRethrowHandler during throw from public static void art.Test1929.throwCatchTestException() @ line = 207" with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" +Test "art.Test1929$DoThrowCatchTestException": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" +Test "art.Test1929$DoThrowCatchTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" +main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 caught class art.Test1929$TestException: throwCatchTestExceptionTwice + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 + public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 222 + public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 226 + public static void art.Test1929.run() throws java.lang.Exception @ line = 280 +Caught art.Test1929$TestException: "throwCatchTestExceptionTwice" +Test "art.Test1929$DoThrowCatchTestExceptionTwice": No error caught with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" +Test "art.Test1929$DoThrowCatchTestExceptionTwice": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" +Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" +main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow + Current Stack: + private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1 + public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60 + private static void art.Test1929.PrintStack() @ line = 52 + public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65 + public static void art.Test1929.run() throws java.lang.Exception @ line = 283 +Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Caught error art.Test1929$TestException:"throwCatchTestExceptionNoRethrow" with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" +Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler" diff --git a/test/1929-exception-catch-exception/info.txt b/test/1929-exception-catch-exception/info.txt new file mode 100644 index 0000000000..2d9b3a0eb3 --- /dev/null +++ b/test/1929-exception-catch-exception/info.txt @@ -0,0 +1 @@ +Test JVMTI behavior when throwing exceptions during the Exception catch Event. diff --git a/test/1929-exception-catch-exception/run b/test/1929-exception-catch-exception/run new file mode 100755 index 0000000000..51875a7e86 --- /dev/null +++ b/test/1929-exception-catch-exception/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/1929-exception-catch-exception/smali/art/Test1929$Impl.smali b/test/1929-exception-catch-exception/smali/art/Test1929$Impl.smali new file mode 100644 index 0000000000..4edd56f690 --- /dev/null +++ b/test/1929-exception-catch-exception/smali/art/Test1929$Impl.smali @@ -0,0 +1,363 @@ +# 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. +# +# The standard dx/jack/d8 all would leave the move-exception instructions outside of either catch +# block. This is different from the RI which will leave the corresponding aload. +# +# See b/65203529 for more information. + +.class public final Lart/Test1929$Impl; +.super Ljava/lang/Object; +.source "Test1929.java" + + +# annotations +.annotation system Ldalvik/annotation/EnclosingClass; + value = Lart/Test1929; +.end annotation + +.annotation system Ldalvik/annotation/InnerClass; + accessFlags = 0x19 + name = "Impl" +.end annotation + + +# direct methods +.method private constructor <init>()V + .registers 1 + + .prologue + .line 152 + invoke-direct {p0}, Ljava/lang/Object;-><init>()V + + return-void +.end method + +.method public static throwCatchBaseTestExceptionTwiceImpl()V + .registers 4 + + .prologue + .line 156 + :try_start_0 + new-instance v1, Lart/Test1929$TestException; + + const-string v2, "throwCatchBaseTestExceptionTwice" + + invoke-direct {v1, v2}, Lart/Test1929$TestException;-><init>(Ljava/lang/String;)V + + throw v1 + :try_end_8 + .catch Lart/Test1929$BaseTestException; {:try_start_0 .. :try_end_8} :catch_8 + + .line 157 + :catch_8 + # This try needs to include the move-exception + :try_start_9 + move-exception v0 + + .line 158 + .local v0, "t":Lart/Test1929$BaseTestException; + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + + new-instance v2, Ljava/lang/StringBuilder; + + invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V + + const-string v3, "Caught " + + invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v2 + + invoke-virtual {v0}, Ljava/lang/Object;->getClass()Ljava/lang/Class; + + move-result-object v3 + + invoke-virtual {v3}, Ljava/lang/Class;->getName()Ljava/lang/String; + + move-result-object v3 + + invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v2 + + const-string v3, ": \"" + + invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v2 + + invoke-virtual {v0}, Lart/Test1929$BaseTestException;->getMessage()Ljava/lang/String; + + move-result-object v3 + + invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v2 + + const-string v3, "\"" + + invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v2 + + invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; + + move-result-object v2 + + invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + + .line 159 + sget-boolean v1, Lart/Test1929;->PRINT_FULL_EXCEPTION:Z + + if-eqz v1, :cond_46 + + .line 160 + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + + invoke-virtual {v0, v1}, Lart/Test1929$BaseTestException;->printStackTrace(Ljava/io/PrintStream;)V + :try_end_46 + .catch Lart/Test1929$BaseTestException; {:try_start_9 .. :try_end_46} :catch_47 + + .line 169 + :cond_46 + :goto_46 + return-void + + .line 163 + :catch_47 + move-exception v0 + + .line 164 + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + + new-instance v2, Ljava/lang/StringBuilder; + + invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V + + const-string v3, "2nd Caught " + + invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v2 + + invoke-virtual {v0}, Ljava/lang/Object;->getClass()Ljava/lang/Class; + + move-result-object v3 + + invoke-virtual {v3}, Ljava/lang/Class;->getName()Ljava/lang/String; + + move-result-object v3 + + invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v2 + + const-string v3, ": \"" + + invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v2 + + invoke-virtual {v0}, Lart/Test1929$BaseTestException;->getMessage()Ljava/lang/String; + + move-result-object v3 + + invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v2 + + const-string v3, "\"" + + invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v2 + + invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; + + move-result-object v2 + + invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + + .line 165 + sget-boolean v1, Lart/Test1929;->PRINT_FULL_EXCEPTION:Z + + if-eqz v1, :cond_46 + + .line 166 + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + + invoke-virtual {v0, v1}, Lart/Test1929$BaseTestException;->printStackTrace(Ljava/io/PrintStream;)V + + goto :goto_46 +.end method + +.method public static throwCatchTestExceptionTwiceImpl()V + .registers 4 + + .prologue + .line 174 + :try_start_0 + new-instance v1, Lart/Test1929$TestException; + + const-string v2, "throwCatchTestExceptionTwice" + + invoke-direct {v1, v2}, Lart/Test1929$TestException;-><init>(Ljava/lang/String;)V + + throw v1 + :try_end_8 + .catch Lart/Test1929$TestException; {:try_start_0 .. :try_end_8} :catch_8 + + .line 175 + :catch_8 + # This try needs to include the move-exception + :try_start_9 + move-exception v0 + + .line 176 + .local v0, "t":Lart/Test1929$TestException; + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + + new-instance v2, Ljava/lang/StringBuilder; + + invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V + + const-string v3, "Caught " + + invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v2 + + invoke-virtual {v0}, Ljava/lang/Object;->getClass()Ljava/lang/Class; + + move-result-object v3 + + invoke-virtual {v3}, Ljava/lang/Class;->getName()Ljava/lang/String; + + move-result-object v3 + + invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v2 + + const-string v3, ": \"" + + invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v2 + + invoke-virtual {v0}, Lart/Test1929$TestException;->getMessage()Ljava/lang/String; + + move-result-object v3 + + invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v2 + + const-string v3, "\"" + + invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v2 + + invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; + + move-result-object v2 + + invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + + .line 177 + sget-boolean v1, Lart/Test1929;->PRINT_FULL_EXCEPTION:Z + + if-eqz v1, :cond_46 + + .line 178 + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + + invoke-virtual {v0, v1}, Lart/Test1929$TestException;->printStackTrace(Ljava/io/PrintStream;)V + :try_end_46 + .catch Lart/Test1929$TestException; {:try_start_9 .. :try_end_46} :catch_47 + + .line 187 + :cond_46 + :goto_46 + return-void + + .line 181 + :catch_47 + move-exception v0 + + .line 182 + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + + new-instance v2, Ljava/lang/StringBuilder; + + invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V + + const-string v3, "2nd Caught " + + invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v2 + + invoke-virtual {v0}, Ljava/lang/Object;->getClass()Ljava/lang/Class; + + move-result-object v3 + + invoke-virtual {v3}, Ljava/lang/Class;->getName()Ljava/lang/String; + + move-result-object v3 + + invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v2 + + const-string v3, ": \"" + + invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v2 + + invoke-virtual {v0}, Lart/Test1929$TestException;->getMessage()Ljava/lang/String; + + move-result-object v3 + + invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v2 + + const-string v3, "\"" + + invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v2 + + invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; + + move-result-object v2 + + invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + + .line 183 + sget-boolean v1, Lart/Test1929;->PRINT_FULL_EXCEPTION:Z + + if-eqz v1, :cond_46 + + .line 184 + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + + invoke-virtual {v0, v1}, Lart/Test1929$TestException;->printStackTrace(Ljava/io/PrintStream;)V + + goto :goto_46 +.end method diff --git a/test/1929-exception-catch-exception/src/Main.java b/test/1929-exception-catch-exception/src/Main.java new file mode 100644 index 0000000000..4651bac3b1 --- /dev/null +++ b/test/1929-exception-catch-exception/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.Test1929.run(); + } +} diff --git a/test/1929-exception-catch-exception/src/art/Breakpoint.java b/test/1929-exception-catch-exception/src/art/Breakpoint.java new file mode 100644 index 0000000000..bbb89f707f --- /dev/null +++ b/test/1929-exception-catch-exception/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/1929-exception-catch-exception/src/art/Exceptions.java b/test/1929-exception-catch-exception/src/art/Exceptions.java new file mode 100644 index 0000000000..2c959ec83f --- /dev/null +++ b/test/1929-exception-catch-exception/src/art/Exceptions.java @@ -0,0 +1,33 @@ +/* + * 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 Exceptions { + public static native void setupExceptionTracing( + Class<?> methodClass, + Class<?> exceptionClass, + Method exceptionEventMethod, + Method exceptionCaughtEventMethod); + + public static native void enableExceptionCatchEvent(Thread thr); + public static native void enableExceptionEvent(Thread thr); + public static native void disableExceptionCatchEvent(Thread thr); + public static native void disableExceptionEvent(Thread thr); +} diff --git a/test/1929-exception-catch-exception/src/art/StackTrace.java b/test/1929-exception-catch-exception/src/art/StackTrace.java new file mode 100644 index 0000000000..b12c3df66b --- /dev/null +++ b/test/1929-exception-catch-exception/src/art/StackTrace.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +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()); + if (suspend_thread) { + Suspension.suspend(thr); + } + StackFrameData[] out = nativeGetStackTrace(thr); + if (suspend_thread) { + Suspension.resume(thr); + } + return out; + } +} + diff --git a/test/1929-exception-catch-exception/src/art/Suspension.java b/test/1929-exception-catch-exception/src/art/Suspension.java new file mode 100644 index 0000000000..16e62ccac9 --- /dev/null +++ b/test/1929-exception-catch-exception/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/1929-exception-catch-exception/src/art/Test1929.java b/test/1929-exception-catch-exception/src/art/Test1929.java new file mode 100644 index 0000000000..07d2087a0f --- /dev/null +++ b/test/1929-exception-catch-exception/src/art/Test1929.java @@ -0,0 +1,300 @@ +/* + * 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.Collection; +import java.util.Objects; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; + +public class Test1929 { + public static boolean PRINT_FULL_EXCEPTION = false; + public static ExceptionHandler HANDLER = null; + public static Collection<Executable> TEST_METHODS; + + public static void doNothing() {}; + static { + try { + TEST_METHODS = Arrays.asList( + Test1929.class.getDeclaredMethod("doThrow"), + Test1929.class.getDeclaredMethod("throwCatchBaseTestException"), + Test1929.class.getDeclaredMethod("throwCatchBaseTestExceptionTwice"), + Test1929.class.getDeclaredMethod("throwCatchTestException"), + Test1929.class.getDeclaredMethod("throwCatchTestExceptionTwice"), + Test1929.class.getDeclaredMethod("throwCatchTestExceptionNoRethrow")); + } catch (Exception e) { + throw new Error("Unable to list test methods!", e); + } + } + + public static interface ExceptionHandler { + public void exceptionOccurred( + Executable m, long loc, Throwable exception); + } + + private static void PrintStack() { + System.out.println("\tCurrent Stack:"); + for (StackTrace.StackFrameData e : StackTrace.GetStackTrace(Thread.currentThread())) { + if (Objects.equals(e.method.getDeclaringClass().getPackage(), Test1929.class.getPackage())) { + System.out.println("\t\t" + e.method + " @ line = " + + Breakpoint.locationToLine(e.method, e.current_location)); + } + } + } + + public static void ExceptionCatchEvent( + Thread thr, Executable method, long location, Throwable exception) { + System.out.println(thr.getName() + ": " + method + " @ line = " + + Breakpoint.locationToLine(method, location) + " caught " + + exception.getClass() + ": " + exception.getMessage()); + PrintStack(); + if (PRINT_FULL_EXCEPTION) { + System.out.print("exception is: "); + exception.printStackTrace(System.out); + } + if (HANDLER != null && TEST_METHODS.contains(method)) { + HANDLER.exceptionOccurred(method, location, exception); + } + } + + public static class BaseTestException extends Error { + public BaseTestException(String e) { super(e); } + public BaseTestException(String e, Throwable t) { super(e, t); } + } + public static class TestException extends BaseTestException { + public TestException(String e) { super(e); } + public TestException(String e, Throwable t) { super(e, t); } + } + + public static class TestExceptionNoRethrow extends TestException { + public TestExceptionNoRethrow(String e) { super(e); } + public TestExceptionNoRethrow(String e, Throwable t) { super(e, t); } + } + + public static class DoNothingHandler implements ExceptionHandler { + public void exceptionOccurred(Executable m, long loc, Throwable exception) { + System.out.println("\tDoing nothing!"); + return; + } + } + + public static class ThrowCatchBase implements ExceptionHandler { + public void exceptionOccurred(Executable m, long loc, Throwable exception) { + System.out.println("\tThrowing BaseTestException and catching it!"); + try { + throw new BaseTestException("ThrowBaseHandler during throw from " + m + " @ line = " + + Breakpoint.locationToLine(m, loc), exception); + } catch (BaseTestException t) { + System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); + if (PRINT_FULL_EXCEPTION) { + t.printStackTrace(System.out); + } + } + } + } + public static class ThrowBaseTestExceptionHandler implements ExceptionHandler { + public void exceptionOccurred(Executable m, long loc, Throwable exception) { + System.out.println("\tThrowing BaseTestException!"); + throw new BaseTestException("ThrowBaseHandler during throw from " + m + " @ line = " + + Breakpoint.locationToLine(m, loc), exception); + } + } + + public static class ThrowTestExceptionNoRethrowHandler implements ExceptionHandler { + public void exceptionOccurred(Executable m, long loc, Throwable exception) { + if (exception instanceof TestExceptionNoRethrow) { + System.out.println("\tInstance of TestExceptionNoRethrow was thrown. Not throwing again."); + } else { + System.out.println("\tThrowing TestExceptionNoRethrow!"); + throw new TestExceptionNoRethrow("ThrowTestExceptionNoRethrowHandler during throw from " + + m + " @ line = " + Breakpoint.locationToLine(m, loc), exception); + } + } + } + public static void doThrow() { + throw new TestException("doThrow"); + } + + public static class DoThrowClass implements Runnable { + public void run() { doThrow(); } + } + + public static void throwCatchBaseTestException() { + try { + throw new TestException("throwCatchBaseTestException"); + } catch (BaseTestException t) { + System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); + if (PRINT_FULL_EXCEPTION) { + t.printStackTrace(System.out); + } + } + } + + public static class DoThrowCatchBaseTestException implements Runnable { + public void run() { throwCatchBaseTestException(); } + } + + // dx/d8/jack all do an optimization around catch blocks that (while legal) breaks assumptions + // this test relies on so we have the actual implementation be corrected smali. This does work + // for RI however. + public static final class Impl { + private Impl() {} + public static void throwCatchBaseTestExceptionTwiceImpl() { + try { + try { + throw new TestException("throwCatchBaseTestExceptionTwice"); + } catch (BaseTestException t) { + System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); + if (PRINT_FULL_EXCEPTION) { + t.printStackTrace(System.out); + } + } + } catch (BaseTestException t) { + System.out.println("2nd Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); + if (PRINT_FULL_EXCEPTION) { + t.printStackTrace(System.out); + } + } + } + + public static void throwCatchTestExceptionTwiceImpl() { + try { + try { + throw new TestException("throwCatchTestExceptionTwice"); + } catch (TestException t) { + System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); + if (PRINT_FULL_EXCEPTION) { + t.printStackTrace(System.out); + } + } + } catch (TestException t) { + System.out.println("2nd Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); + if (PRINT_FULL_EXCEPTION) { + t.printStackTrace(System.out); + } + } + } + } + + public static void throwCatchBaseTestExceptionTwice() { + // The implementation of this has to change depending upon the runtime slightly due to compiler + // optimizations present in DX/D8/Jack. + Impl.throwCatchBaseTestExceptionTwiceImpl(); + } + + public static class DoThrowCatchBaseTestExceptionTwice implements Runnable { + public void run() { throwCatchBaseTestExceptionTwice(); } + } + + public static void throwCatchTestException() { + try { + throw new TestException("throwCatchTestException"); + } catch (TestException t) { + System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); + if (PRINT_FULL_EXCEPTION) { + t.printStackTrace(System.out); + } + } + } + + public static class DoThrowCatchTestException implements Runnable { + public void run() { throwCatchTestException(); } + } + + public static void throwCatchTestExceptionTwice() { + // The implementation of this has to change depending upon the runtime slightly due to compiler + // optimizations present in DX/D8/Jack. + Impl.throwCatchTestExceptionTwiceImpl(); + } + + public static class DoThrowCatchTestExceptionTwice implements Runnable { + public void run() { throwCatchTestExceptionTwice(); } + } + + public static void throwCatchTestExceptionNoRethrow() { + try { + throw new TestException("throwCatchTestExceptionNoRethrow"); + } catch (TestExceptionNoRethrow t) { + System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\""); + if (PRINT_FULL_EXCEPTION) { + t.printStackTrace(System.out); + } + } + } + + public static class DoThrowCatchTestExceptionNoRethrow implements Runnable { + public void run() { throwCatchTestExceptionNoRethrow(); } + } + + public static void run() throws Exception { + // Set up breakpoints + Exceptions.setupExceptionTracing( + Test1929.class, + TestException.class, + null, + Test1929.class.getDeclaredMethod( + "ExceptionCatchEvent", + Thread.class, + Executable.class, + Long.TYPE, + Throwable.class)); + Exceptions.enableExceptionCatchEvent(Thread.currentThread()); + + ExceptionHandler[] handlers = new ExceptionHandler[] { + new DoNothingHandler(), + new ThrowCatchBase(), + new ThrowBaseTestExceptionHandler(), + new ThrowTestExceptionNoRethrowHandler(), + }; + + Runnable[] tests = new Runnable[] { + new DoThrowClass(), + new DoThrowCatchBaseTestException(), + new DoThrowCatchBaseTestExceptionTwice(), + new DoThrowCatchTestException(), + new DoThrowCatchTestExceptionTwice(), + new DoThrowCatchTestExceptionNoRethrow(), + }; + + for (ExceptionHandler handler : handlers) { + for (Runnable test : tests) { + try { + HANDLER = handler; + System.out.printf("Test \"%s\": Running breakpoint with handler \"%s\"\n", + test.getClass().getName(), handler.getClass().getName()); + test.run(); + System.out.printf("Test \"%s\": No error caught with handler \"%s\"\n", + test.getClass().getName(), handler.getClass().getName()); + } catch (Throwable e) { + System.out.printf("Test \"%s\": Caught error %s:\"%s\" with handler \"%s\"\n", + test.getClass().getName(), + e.getClass().getName(), + e.getMessage(), + handler.getClass().getName()); + if (PRINT_FULL_EXCEPTION) { + e.printStackTrace(System.out); + } + } + System.out.printf("Test \"%s\": Finished running with handler \"%s\"\n", + test.getClass().getName(), handler.getClass().getName()); + HANDLER = null; + } + } + Exceptions.disableExceptionCatchEvent(Thread.currentThread()); + } +} diff --git a/test/1930-monitor-info/expected.txt b/test/1930-monitor-info/expected.txt new file mode 100644 index 0000000000..b43f1b20b6 --- /dev/null +++ b/test/1930-monitor-info/expected.txt @@ -0,0 +1,31 @@ +Running with single thread. +Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testSingleThread], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] } +Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testSingleThread], owner: main, entryCount: 1, waiters: [], notify_waiters: [] } +Running with single thread in native. +Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testSingleThread], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] } +Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testSingleThread], owner: main, entryCount: 1, waiters: [], notify_waiters: [] } +Lock twice +Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwice], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] } +Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwice], owner: main, entryCount: 1, waiters: [], notify_waiters: [] } +Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwice], owner: main, entryCount: 2, waiters: [], notify_waiters: [] } +Lock twice native +Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNative], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] } +Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNative], owner: main, entryCount: 1, waiters: [], notify_waiters: [] } +Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNative], owner: main, entryCount: 2, waiters: [], notify_waiters: [] } +Lock twice Java then native +Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceJN], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] } +Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceJN], owner: main, entryCount: 1, waiters: [], notify_waiters: [] } +Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceJN], owner: main, entryCount: 2, waiters: [], notify_waiters: [] } +Lock twice native then Java +Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNJ], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] } +Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNJ], owner: main, entryCount: 1, waiters: [], notify_waiters: [] } +Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNJ], owner: main, entryCount: 2, waiters: [], notify_waiters: [] } +lock with wait +Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockWait], owner: main, entryCount: 1, waiters: [Test1930 Thread - testLockWait], notify_waiters: [] } +Thread[Test1930 Thread - testLockWait]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockWait], owner: Test1930 Thread - testLockWait, entryCount: 1, waiters: [], notify_waiters: [] } +Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockWait], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] } +Wait for notify. +Thread[Test1930 Thread - testLockWait]: MonitorUsage{ monitor: NamedLock[Test1930 - testNotifyWait], owner: Test1930 Thread - testLockWait, entryCount: 1, waiters: [], notify_waiters: [] } +Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testNotifyWait], owner: main, entryCount: 1, waiters: [Test1930 Thread - testLockWait], notify_waiters: [Test1930 Thread - testLockWait] } +Thread[Test1930 Thread - testLockWait]: MonitorUsage{ monitor: NamedLock[Test1930 - testNotifyWait], owner: Test1930 Thread - testLockWait, entryCount: 1, waiters: [], notify_waiters: [] } +Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testNotifyWait], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] } diff --git a/test/1930-monitor-info/info.txt b/test/1930-monitor-info/info.txt new file mode 100644 index 0000000000..8e19edce92 --- /dev/null +++ b/test/1930-monitor-info/info.txt @@ -0,0 +1,3 @@ +Tests basic functions in the jvmti plugin. + +Tests that the GetObjectMonitorUsage function works correctly. diff --git a/test/1930-monitor-info/monitor.cc b/test/1930-monitor-info/monitor.cc new file mode 100644 index 0000000000..7f97c05a73 --- /dev/null +++ b/test/1930-monitor-info/monitor.cc @@ -0,0 +1,67 @@ +/* + * 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 Test1930MonitorInfo { + +extern "C" JNIEXPORT void JNICALL Java_art_Test1930_executeLockedNative(JNIEnv* env, + jclass klass, + jobject run, + jobject l) { + ScopedLocalRef<jclass> runnable(env, env->FindClass("java/lang/Runnable")); + if (env->ExceptionCheck()) { + return; + } + jmethodID method = env->GetMethodID(runnable.get(), "run", "()V"); + + if (env->ExceptionCheck()) { + return; + } + jmethodID printMethod = env->GetStaticMethodID(klass, "printPreLock", "(Ljava/lang/Object;)V"); + if (env->ExceptionCheck()) { + return; + } + + env->CallStaticVoidMethod(klass, printMethod, l); + if (env->ExceptionCheck()) { + return; + } + if (env->MonitorEnter(l) != 0) { + return; + } + env->CallVoidMethod(run, method); + env->MonitorExit(l); +} + +} // namespace Test1930MonitorInfo +} // namespace art diff --git a/test/1930-monitor-info/run b/test/1930-monitor-info/run new file mode 100755 index 0000000000..e92b873956 --- /dev/null +++ b/test/1930-monitor-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/1930-monitor-info/src/Main.java b/test/1930-monitor-info/src/Main.java new file mode 100644 index 0000000000..332846133b --- /dev/null +++ b/test/1930-monitor-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.Test1930.run(); + } +} diff --git a/test/1930-monitor-info/src/art/Monitors.java b/test/1930-monitor-info/src/art/Monitors.java new file mode 100644 index 0000000000..26f7718dc5 --- /dev/null +++ b/test/1930-monitor-info/src/art/Monitors.java @@ -0,0 +1,72 @@ +/* + * 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.Objects; +import java.util.function.Function; +import java.util.stream.Stream; + +public class Monitors { + public static class NamedLock { + public final String name; + public NamedLock(String name) { + this.name = name; + } + public String toString() { + return String.format("NamedLock[%s]", name); + } + } + + public static final class MonitorUsage { + public final Object monitor; + public final Thread owner; + public final int entryCount; + public final Thread[] waiters; + public final Thread[] notifyWaiters; + + public MonitorUsage( + Object monitor, + Thread owner, + int entryCount, + Thread[] waiters, + Thread[] notifyWaiters) { + this.monitor = monitor; + this.entryCount = entryCount; + this.owner = owner; + this.waiters = waiters; + this.notifyWaiters = notifyWaiters; + } + + private static String toNameList(Thread[] ts) { + return Arrays.toString(Arrays.stream(ts).map((Thread t) -> t.getName()).toArray()); + } + + public String toString() { + return String.format( + "MonitorUsage{ monitor: %s, owner: %s, entryCount: %d, waiters: %s, notify_waiters: %s }", + monitor, + (owner != null) ? owner.getName() : "<NULL>", + entryCount, + toNameList(waiters), + toNameList(notifyWaiters)); + } + } + + public static native MonitorUsage getObjectMonitorUsage(Object monitor); +} + diff --git a/test/1930-monitor-info/src/art/Test1930.java b/test/1930-monitor-info/src/art/Test1930.java new file mode 100644 index 0000000000..a7fa1c78cb --- /dev/null +++ b/test/1930-monitor-info/src/art/Test1930.java @@ -0,0 +1,155 @@ +/* + * 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.concurrent.Semaphore; +import java.util.Arrays; + +public class Test1930 { + public static final int NUM_RETRY = 100; + private static void testSingleThread() { + Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testSingleThread"); + executeLocked(() -> { printMonitorUsage(lk); }, lk); + } + private static void testSingleThreadNative() { + Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testSingleThread"); + executeLockedNative(() -> { printMonitorUsage(lk); }, lk); + } + + private static void testLockedTwice() { + final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockedTwice"); + executeLocked(() -> { executeLocked(() -> { printMonitorUsage(lk); }, lk); }, lk); + } + + private static void testLockedTwiceNJ() { + final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockedTwiceNJ"); + executeLockedNative(() -> { executeLockedNative(() -> { printMonitorUsage(lk); }, lk); }, lk); + } + + private static void testLockedTwiceJN() { + final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockedTwiceJN"); + executeLockedNative(() -> { executeLockedNative(() -> { printMonitorUsage(lk); }, lk); }, lk); + } + + private static void testLockedTwiceNative() { + final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockedTwiceNative"); + executeLockedNative(() -> { executeLockedNative(() -> { printMonitorUsage(lk); }, lk); }, lk); + } + + public final static class ThreadSignaler { + public volatile boolean signal = false; + } + + private static void testLockWait() throws Exception { + final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockWait"); + final Semaphore sem = new Semaphore(0); + final Thread t = new Thread(() -> { + sem.release(); + synchronized (lk) { + printMonitorUsage(lk); + } + }, "Test1930 Thread - testLockWait"); + synchronized (lk) { + t.start(); + // Wait for the other thread to actually start. + sem.acquire(); + // Wait for the other thread to go to sleep trying to get the mutex. This might take a (short) + // time since we try spinning first for better performance. + boolean found_wait = false; + for (long i = 0; i < NUM_RETRY; i++) { + if (Arrays.asList(Monitors.getObjectMonitorUsage(lk).waiters).contains(t)) { + found_wait = true; + break; + } else { + Thread.sleep(500); + Thread.yield(); + } + } + if (!found_wait) { + System.out.println("other thread doesn't seem to be waiting."); + } + printMonitorUsage(lk); + } + t.join(); + printMonitorUsage(lk); + } + + private static void testNotifyWait() throws Exception { + final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testNotifyWait"); + final Semaphore sem = new Semaphore(0); + Thread t = new Thread(() -> { + synchronized (lk) { + printMonitorUsage(lk); + sem.release(); + try { + lk.wait(); + } catch (Exception e) { + throw new Error("Error waiting!", e); + } + printMonitorUsage(lk); + } + }, "Test1930 Thread - testLockWait"); + t.start(); + sem.acquire(); + synchronized (lk) { + printMonitorUsage(lk); + lk.notifyAll(); + } + t.join(); + printMonitorUsage(lk); + } + + public static void run() throws Exception { + // Single threaded tests. + System.out.println("Running with single thread."); + testSingleThread(); + System.out.println("Running with single thread in native."); + testSingleThreadNative(); + System.out.println("Lock twice"); + testLockedTwice(); + System.out.println("Lock twice native"); + testLockedTwiceNative(); + System.out.println("Lock twice Java then native"); + testLockedTwiceJN(); + System.out.println("Lock twice native then Java"); + testLockedTwiceNJ(); + + // Mutli threaded tests. + System.out.println("lock with wait"); + testLockWait(); + System.out.println("Wait for notify."); + testNotifyWait(); + } + + public static void printPreLock(Object lock) { + System.out.println(String.format("Pre-lock[%s]: %s", + Thread.currentThread().getName(), Monitors.getObjectMonitorUsage(lock))); + } + + public static void executeLocked(Runnable r, Object lock) { + printPreLock(lock); + synchronized (lock) { + r.run(); + } + } + + public native static void executeLockedNative(Runnable r, Object m); + public static void printMonitorUsage(Object m) { + System.out.println(String.format("Thread[%s]: %s", + Thread.currentThread().getName(), Monitors.getObjectMonitorUsage(m))); + } +} diff --git a/test/441-checker-inliner/smali/Smali.smali b/test/441-checker-inliner/smali/Smali.smali new file mode 100644 index 0000000000..7e427551d4 --- /dev/null +++ b/test/441-checker-inliner/smali/Smali.smali @@ -0,0 +1,97 @@ +# 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. + +.class public LSmali; +.super Ljava/lang/Object; +.source "Smali.java" + +## CHECK-START: int Smali.InlineWithControlFlow(boolean) inliner (before) +## CHECK-DAG: <<Const1:i\d+>> IntConstant 1 +## CHECK-DAG: <<Const3:i\d+>> IntConstant 3 +## CHECK-DAG: <<Const5:i\d+>> IntConstant 5 +## CHECK-DAG: <<Add:i\d+>> InvokeStaticOrDirect [<<Const1>>,<<Const3>>{{(,[ij]\d+)?}}] +## CHECK-DAG: <<Sub:i\d+>> InvokeStaticOrDirect [<<Const5>>,<<Const3>>{{(,[ij]\d+)?}}] +## CHECK-DAG: <<Phi:i\d+>> Phi [<<Add>>,<<Sub>>] +## CHECK-DAG: Return [<<Phi>>] + +## CHECK-START: int Smali.InlineWithControlFlow(boolean) inliner (after) +## CHECK-DAG: <<Const4:i\d+>> IntConstant 4 +## CHECK-DAG: <<Const2:i\d+>> IntConstant 2 +## CHECK-DAG: <<Phi:i\d+>> Phi [<<Const4>>,<<Const2>>] +## CHECK-DAG: Return [<<Phi>>] +.method public static InlineWithControlFlow(Z)I + + # int x, const1, const3, const5; + # const1 = 1; + # const3 = 3; + # const5 = 5; + # if (cond) { + # x = returnAdd(const1, const3); + # } else { + # x = returnSub(const5, const3); + # } + # return x; + + .registers 5 + .param p0, "cond" # Z + + .prologue + const/4 v0, 0x1 + + .local v0, "const1":I + const/4 v1, 0x3 + + .local v1, "const3":I + const/4 v2, 0x5 + + .local v2, "const5":I + if-eqz p0, :cond_a + + invoke-static {v0, v1}, LSmali;->returnAdd(II)I + + move-result v3 + + .local v3, "x":I + :goto_9 + return v3 + + .end local v3 # "x":I + :cond_a + invoke-static {v2, v1}, LSmali;->returnSub(II)I + + move-result v3 + + .restart local v3 # "x":I + goto :goto_9 +.end method + +.method private static returnAdd(II)I + .registers 3 + .param p0, "a" # I + .param p1, "b" # I + + add-int v0, p0, p1 + + return v0 +.end method + +.method private static returnSub(II)I + .registers 3 + .param p0, "a" # I + .param p1, "b" # I + + sub-int v0, p0, p1 + + return v0 +.end method diff --git a/test/441-checker-inliner/src/Main.java b/test/441-checker-inliner/src/Main.java index 6d6a4f2186..aff4a0717e 100644 --- a/test/441-checker-inliner/src/Main.java +++ b/test/441-checker-inliner/src/Main.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import java.lang.reflect.Method; public class Main { @@ -124,32 +125,14 @@ public class Main { return incCounter(); } - /// CHECK-START: int Main.InlineWithControlFlow(boolean) inliner (before) - /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 - /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3 - /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5 - /// CHECK-DAG: <<Add:i\d+>> InvokeStaticOrDirect [<<Const1>>,<<Const3>>{{(,[ij]\d+)?}}] - /// CHECK-DAG: <<Sub:i\d+>> InvokeStaticOrDirect [<<Const5>>,<<Const3>>{{(,[ij]\d+)?}}] - /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Add>>,<<Sub>>] - /// CHECK-DAG: Return [<<Phi>>] - - /// CHECK-START: int Main.InlineWithControlFlow(boolean) inliner (after) - /// CHECK-DAG: <<Const4:i\d+>> IntConstant 4 - /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2 - /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const4>>,<<Const2>>] - /// CHECK-DAG: Return [<<Phi>>] - public static int InlineWithControlFlow(boolean cond) { - int x, const1, const3, const5; - const1 = 1; - const3 = 3; - const5 = 5; - if (cond) { - x = returnAdd(const1, const3); - } else { - x = returnSub(const5, const3); + try { + Class<?> c = Class.forName("Smali"); + Method m = c.getMethod("InlineWithControlFlow", boolean.class); + return (Integer) m.invoke(null, cond); + } catch (Throwable t) { + throw new RuntimeException(t); } - return x; } /// CHECK-START: int Main.returnAbs(int) intrinsics_recognition (before) diff --git a/test/455-checker-gvn/smali/Smali.smali b/test/455-checker-gvn/smali/Smali.smali new file mode 100644 index 0000000000..fbeb4dbc8e --- /dev/null +++ b/test/455-checker-gvn/smali/Smali.smali @@ -0,0 +1,42 @@ +# 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. +.class public LSmali; +.super Ljava/lang/Object; + +## CHECK-START: int Smali.foo(int, int) GVN (before) +## CHECK: Add +## CHECK: Add +## CHECK: Add + +## CHECK-START: int Smali.foo(int, int) GVN (after) +## CHECK: Add +## CHECK: Add +## CHECK-NOT: Add +.method public static foo(II)I + + # int sum1 = x + y; + # int sum2 = y + x; + # return sum1 + sum2; + + .registers 5 + .param p0, "x" # I + .param p1, "y" # I + + add-int v0, p0, p1 + add-int v1, p1, p0 + add-int v2, v0, v1 + + return v2 +.end method + diff --git a/test/455-checker-gvn/src/Main.java b/test/455-checker-gvn/src/Main.java index cea0959979..6df57fdeef 100644 --- a/test/455-checker-gvn/src/Main.java +++ b/test/455-checker-gvn/src/Main.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import java.lang.reflect.Method; public class Main { @@ -25,20 +26,14 @@ public class Main { System.out.println(directIntrinsic(-5)); } - /// CHECK-START: int Main.foo(int, int) GVN (before) - /// CHECK: Add - /// CHECK: Add - /// CHECK: Add - - /// CHECK-START: int Main.foo(int, int) GVN (after) - /// CHECK: Add - /// CHECK: Add - /// CHECK-NOT: Add - public static int foo(int x, int y) { - int sum1 = x + y; - int sum2 = y + x; - return sum1 + sum2; + try { + Class<?> c = Class.forName("Smali"); + Method m = c.getMethod("foo", int.class, int.class); + return (Integer) m.invoke(null, x, y); + } catch (Throwable t) { + throw new RuntimeException(t); + } } /// CHECK-START: int Main.mulAndIntrinsic() GVN (before) diff --git a/test/480-checker-dead-blocks/smali/Smali.smali b/test/480-checker-dead-blocks/smali/Smali.smali new file mode 100644 index 0000000000..b93d5ac886 --- /dev/null +++ b/test/480-checker-dead-blocks/smali/Smali.smali @@ -0,0 +1,122 @@ +# 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. + +.class public LSmali; +.super Ljava/lang/Object; + +## CHECK-START: int Smali.testTrueBranch(int, int) dead_code_elimination$after_inlining (before) +## CHECK-DAG: <<ArgX:i\d+>> ParameterValue +## CHECK-DAG: <<ArgY:i\d+>> ParameterValue +## CHECK-DAG: If +## CHECK-DAG: <<Add:i\d+>> Add [<<ArgX>>,<<ArgY>>] +## CHECK-DAG: <<Sub:i\d+>> Sub [<<ArgX>>,<<ArgY>>] +## CHECK-DAG: <<Phi:i\d+>> Phi [<<Add>>,<<Sub>>] +## CHECK-DAG: Return [<<Phi>>] + +## CHECK-START: int Smali.testTrueBranch(int, int) dead_code_elimination$after_inlining (after) +## CHECK-DAG: <<ArgX:i\d+>> ParameterValue +## CHECK-DAG: <<ArgY:i\d+>> ParameterValue +## CHECK-DAG: <<Add:i\d+>> Add [<<ArgX>>,<<ArgY>>] +## CHECK-DAG: Return [<<Add>>] + +## CHECK-START: int Smali.testTrueBranch(int, int) dead_code_elimination$after_inlining (after) +## CHECK-NOT: If +## CHECK-NOT: Sub +## CHECK-NOT: Phi +.method public static testTrueBranch(II)I + # int z; + # if (inlineTrue()) { + # z = x + y; + # } else { + # z = x - y; + # // Prevent HSelect simplification by having a branch with multiple instructions. + # System.nanoTime(); + # } + #return z; + + .registers 4 + .param p0, "x" # I + .param p1, "y" # I + + invoke-static {}, LMain;->inlineTrue()Z + + move-result v1 + + if-eqz v1, :cond_9 + + add-int v0, p0, p1 + + :goto_8 + return v0 + + :cond_9 + sub-int v0, p0, p1 + + invoke-static {}, Ljava/lang/System;->nanoTime()J + + goto :goto_8 +.end method + +## CHECK-START: int Smali.testFalseBranch(int, int) dead_code_elimination$after_inlining (before) +## CHECK-DAG: <<ArgX:i\d+>> ParameterValue +## CHECK-DAG: <<ArgY:i\d+>> ParameterValue +## CHECK-DAG: If +## CHECK-DAG: <<Add:i\d+>> Add [<<ArgX>>,<<ArgY>>] +## CHECK-DAG: <<Sub:i\d+>> Sub [<<ArgX>>,<<ArgY>>] +## CHECK-DAG: <<Phi:i\d+>> Phi [<<Add>>,<<Sub>>] +## CHECK-DAG: Return [<<Phi>>] + +## CHECK-START: int Smali.testFalseBranch(int, int) dead_code_elimination$after_inlining (after) +## CHECK-DAG: <<ArgX:i\d+>> ParameterValue +## CHECK-DAG: <<ArgY:i\d+>> ParameterValue +## CHECK-DAG: <<Sub:i\d+>> Sub [<<ArgX>>,<<ArgY>>] +## CHECK-DAG: Return [<<Sub>>] + +## CHECK-START: int Smali.testFalseBranch(int, int) dead_code_elimination$after_inlining (after) +## CHECK-NOT: If +## CHECK-NOT: Add +## CHECK-NOT: Phi +.method public static testFalseBranch(II)I + # int z; + # if (inlineFalse()) { + # z = x + y; + # } else { + # z = x - y; + # // Prevent HSelect simplification by having a branch with multiple instructions. + # System.nanoTime(); + # } + # return z; + + .registers 4 + .param p0, "x" # I + .param p1, "y" # I + + invoke-static {}, LMain;->inlineFalse()Z + + move-result v1 + + if-eqz v1, :cond_9 + + add-int v0, p0, p1 + + :goto_8 + return v0 + + :cond_9 + sub-int v0, p0, p1 + + invoke-static {}, Ljava/lang/System;->nanoTime()J + + goto :goto_8 +.end method diff --git a/test/480-checker-dead-blocks/src/Main.java b/test/480-checker-dead-blocks/src/Main.java index 0ca822f54d..485efd9a2f 100644 --- a/test/480-checker-dead-blocks/src/Main.java +++ b/test/480-checker-dead-blocks/src/Main.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import java.lang.reflect.Method; public class Main { @@ -30,68 +31,24 @@ public class Main { return false; } - /// CHECK-START: int Main.testTrueBranch(int, int) dead_code_elimination$after_inlining (before) - /// CHECK-DAG: <<ArgX:i\d+>> ParameterValue - /// CHECK-DAG: <<ArgY:i\d+>> ParameterValue - /// CHECK-DAG: If - /// CHECK-DAG: <<Add:i\d+>> Add [<<ArgX>>,<<ArgY>>] - /// CHECK-DAG: <<Sub:i\d+>> Sub [<<ArgX>>,<<ArgY>>] - /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Add>>,<<Sub>>] - /// CHECK-DAG: Return [<<Phi>>] - - /// CHECK-START: int Main.testTrueBranch(int, int) dead_code_elimination$after_inlining (after) - /// CHECK-DAG: <<ArgX:i\d+>> ParameterValue - /// CHECK-DAG: <<ArgY:i\d+>> ParameterValue - /// CHECK-DAG: <<Add:i\d+>> Add [<<ArgX>>,<<ArgY>>] - /// CHECK-DAG: Return [<<Add>>] - - /// CHECK-START: int Main.testTrueBranch(int, int) dead_code_elimination$after_inlining (after) - /// CHECK-NOT: If - /// CHECK-NOT: Sub - /// CHECK-NOT: Phi - public static int testTrueBranch(int x, int y) { - int z; - if (inlineTrue()) { - z = x + y; - } else { - z = x - y; - // Prevent HSelect simplification by having a branch with multiple instructions. - System.nanoTime(); + try { + Class<?> c = Class.forName("Smali"); + Method m = c.getMethod("testTrueBranch", int.class, int.class); + return (Integer) m.invoke(null, x, y); + } catch (Throwable t) { + throw new RuntimeException(t); } - return z; } - /// CHECK-START: int Main.testFalseBranch(int, int) dead_code_elimination$after_inlining (before) - /// CHECK-DAG: <<ArgX:i\d+>> ParameterValue - /// CHECK-DAG: <<ArgY:i\d+>> ParameterValue - /// CHECK-DAG: If - /// CHECK-DAG: <<Add:i\d+>> Add [<<ArgX>>,<<ArgY>>] - /// CHECK-DAG: <<Sub:i\d+>> Sub [<<ArgX>>,<<ArgY>>] - /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Add>>,<<Sub>>] - /// CHECK-DAG: Return [<<Phi>>] - - /// CHECK-START: int Main.testFalseBranch(int, int) dead_code_elimination$after_inlining (after) - /// CHECK-DAG: <<ArgX:i\d+>> ParameterValue - /// CHECK-DAG: <<ArgY:i\d+>> ParameterValue - /// CHECK-DAG: <<Sub:i\d+>> Sub [<<ArgX>>,<<ArgY>>] - /// CHECK-DAG: Return [<<Sub>>] - - /// CHECK-START: int Main.testFalseBranch(int, int) dead_code_elimination$after_inlining (after) - /// CHECK-NOT: If - /// CHECK-NOT: Add - /// CHECK-NOT: Phi - public static int testFalseBranch(int x, int y) { - int z; - if (inlineFalse()) { - z = x + y; - } else { - z = x - y; - // Prevent HSelect simplification by having a branch with multiple instructions. - System.nanoTime(); + try { + Class<?> c = Class.forName("Smali"); + Method m = c.getMethod("testFalseBranch", int.class, int.class); + return (Integer) m.invoke(null, x, y); + } catch (Throwable t) { + throw new RuntimeException(t); } - return z; } /// CHECK-START: int Main.testRemoveLoop(int) dead_code_elimination$after_inlining (before) diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java index 663250369d..7ae873af54 100644 --- a/test/530-checker-lse/src/Main.java +++ b/test/530-checker-lse/src/Main.java @@ -881,10 +881,10 @@ public class Main { /// CHECK: ArrayGet private static int testAllocationEliminationOfArray2() { // Cannot eliminate array allocation since array is accessed with non-constant - // index. - int[] array = new int[4]; - array[2] = 4; - array[3] = 7; + // index (only 3 elements to prevent vectorization of the reduction). + int[] array = new int[3]; + array[1] = 4; + array[2] = 7; int sum = 0; for (int e : array) { sum += e; diff --git a/test/661-checker-simd-reduc/src/Main.java b/test/661-checker-simd-reduc/src/Main.java index 741b5fa9a5..8208a9e7b5 100644 --- a/test/661-checker-simd-reduc/src/Main.java +++ b/test/661-checker-simd-reduc/src/Main.java @@ -51,6 +51,26 @@ public class Main { 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 @@ public class Main { 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 @@ public class Main { 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 @@ public class Main { 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 @@ public class Main { 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 @@ public class Main { 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 @@ public class Main { 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 @@ public class Main { 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/665-checker-simd-zero/expected.txt b/test/665-checker-simd-zero/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/665-checker-simd-zero/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/665-checker-simd-zero/info.txt b/test/665-checker-simd-zero/info.txt new file mode 100644 index 0000000000..55eca88b04 --- /dev/null +++ b/test/665-checker-simd-zero/info.txt @@ -0,0 +1 @@ +Functional tests on zero-out SIMD vectorization. diff --git a/test/665-checker-simd-zero/src/Main.java b/test/665-checker-simd-zero/src/Main.java new file mode 100644 index 0000000000..66eea642a4 --- /dev/null +++ b/test/665-checker-simd-zero/src/Main.java @@ -0,0 +1,236 @@ +/* + * 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. + */ + +/** + * Tests for zero vectorization. + */ +public class Main { + + /// CHECK-START: void Main.zeroz(boolean[]) loop_optimization (before) + /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START-ARM64: void Main.zeroz(boolean[]) loop_optimization (after) + /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none + private static void zeroz(boolean[] x) { + for (int i = 0; i < x.length; i++) { + x[i] = false; + } + } + + /// CHECK-START: void Main.zerob(byte[]) loop_optimization (before) + /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START-ARM64: void Main.zerob(byte[]) loop_optimization (after) + /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none + private static void zerob(byte[] x) { + for (int i = 0; i < x.length; i++) { + x[i] = 0; + } + } + + /// CHECK-START: void Main.zeroc(char[]) loop_optimization (before) + /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START-ARM64: void Main.zeroc(char[]) loop_optimization (after) + /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none + private static void zeroc(char[] x) { + for (int i = 0; i < x.length; i++) { + x[i] = 0; + } + } + + /// CHECK-START: void Main.zeros(short[]) loop_optimization (before) + /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START-ARM64: void Main.zeros(short[]) loop_optimization (after) + /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none + private static void zeros(short[] x) { + for (int i = 0; i < x.length; i++) { + x[i] = 0; + } + } + + /// CHECK-START: void Main.zeroi(int[]) loop_optimization (before) + /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START-ARM64: void Main.zeroi(int[]) loop_optimization (after) + /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none + private static void zeroi(int[] x) { + for (int i = 0; i < x.length; i++) { + x[i] = 0; + } + } + + /// CHECK-START: void Main.zerol(long[]) loop_optimization (before) + /// CHECK-DAG: <<Zero:j\d+>> LongConstant 0 loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START-ARM64: void Main.zerol(long[]) loop_optimization (after) + /// CHECK-DAG: <<Zero:j\d+>> LongConstant 0 loop:none + /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none + private static void zerol(long[] x) { + for (int i = 0; i < x.length; i++) { + x[i] = 0; + } + } + + /// CHECK-START: void Main.zerof(float[]) loop_optimization (before) + /// CHECK-DAG: <<Zero:f\d+>> FloatConstant 0 loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START-ARM64: void Main.zerof(float[]) loop_optimization (after) + /// CHECK-DAG: <<Zero:f\d+>> FloatConstant 0 loop:none + /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none + private static void zerof(float[] x) { + for (int i = 0; i < x.length; i++) { + x[i] = 0; + } + } + + /// CHECK-START: void Main.zerod(double[]) loop_optimization (before) + /// CHECK-DAG: <<Zero:d\d+>> DoubleConstant 0 loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START-ARM64: void Main.zerod(double[]) loop_optimization (after) + /// CHECK-DAG: <<Zero:d\d+>> DoubleConstant 0 loop:none + /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>] loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none + private static void zerod(double[] x) { + for (int i = 0; i < x.length; i++) { + x[i] = 0; + } + } + + public static void main(String[] args) { + int total = 1111; + + boolean[] xz = new boolean[total]; + byte[] xb = new byte[total]; + char[] xc = new char[total]; + short[] xs = new short[total]; + int[] xi = new int[total]; + long[] xl = new long[total]; + float[] xf = new float[total]; + double[] xd = new double[total]; + + for (int i = 0; i < total; i++) { + xz[i] = true; + xb[i] = 1; + xc[i] = 1; + xs[i] = 1; + xi[i] = 1; + xl[i] = 1; + xf[i] = 1; + xd[i] = 1; + } + + for (int i = 0; i < total; i++) { + expectEquals(true, xz[i]); + expectEquals(1, xb[i]); + expectEquals(1, xc[i]); + expectEquals(1, xs[i]); + expectEquals(1, xi[i]); + expectEquals(1, xl[i]); + expectEquals(1, xf[i]); + expectEquals(1, xd[i]); + } + + zeroz(xz); + zerob(xb); + zeroc(xc); + zeros(xs); + zeroi(xi); + zerol(xl); + zerof(xf); + zerod(xd); + + for (int i = 0; i < total; i++) { + expectEquals(false, xz[i]); + expectEquals(0, xb[i]); + expectEquals(0, xc[i]); + expectEquals(0, xs[i]); + expectEquals(0, xi[i]); + expectEquals(0, xl[i]); + expectEquals(0, xf[i]); + expectEquals(0, xd[i]); + } + + System.out.println("passed"); + } + + private static void expectEquals(boolean expected, boolean result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + private static void expectEquals(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + private static void expectEquals(float expected, float result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + private static void expectEquals(double expected, double result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } +} diff --git a/test/Android.bp b/test/Android.bp index 2aed50c455..2f2305607e 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -250,10 +250,12 @@ art_cc_defaults { "ti-agent/common_helper.cc", "ti-agent/frame_pop_helper.cc", "ti-agent/locals_helper.cc", + "ti-agent/monitors_helper.cc", "ti-agent/redefinition_helper.cc", "ti-agent/suspension_helper.cc", "ti-agent/stack_trace_helper.cc", "ti-agent/trace_helper.cc", + "ti-agent/exceptions_helper.cc", // This is the list of non-special OnLoad things and excludes BCI and anything that depends // on ART internals. "903-hello-tagging/tagging.cc", @@ -298,6 +300,8 @@ art_cc_defaults { "1922-owned-monitors-info/owned_monitors.cc", "1924-frame-pop-toggle/frame_pop_toggle.cc", "1926-missed-frame-pop/frame_pop_missed.cc", + "1927-exception-event/exception_event.cc", + "1930-monitor-info/monitor.cc", ], shared_libs: [ "libbase", @@ -347,6 +351,7 @@ art_cc_defaults { ], shared_libs: [ "libbase", + "slicer", ], header_libs: ["libopenjdkjvmti_headers"], } diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 90e260012a..c16c487dd1 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -422,10 +422,7 @@ if [[ "$JVMTI_STRESS" = "y" ]]; then if [[ "$JVMTI_REDEFINE_STRESS" = "y" ]]; then # We really cannot do this on RI so don't both passing it in that case. if [[ "$USE_JVM" = "n" ]]; then - file_1=$(mktemp --tmpdir=${DEX_LOCATION}) - file_2=$(mktemp --tmpdir=${DEX_LOCATION}) - # TODO Remove need for DEXTER_BINARY! - agent_args="${agent_args},redefine,${DEXTER_BINARY},${file_1},${file_2}" + agent_args="${agent_args},redefine" fi fi if [[ "$JVMTI_FIELD_STRESS" = "y" ]]; then diff --git a/test/knownfailures.json b/test/knownfailures.json index 09c374367a..84758c9f54 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -267,6 +267,11 @@ "variant": "npictest" }, { + "tests": "639-checker-code-sinking", + "variant": "pictest", + "bug": "http://b/65366606" + }, + { "tests": "055-enum-performance", "variant": "optimizing | regalloc_gc", "description": ["055: Exceeds run time limits due to heap poisoning ", diff --git a/test/ti-agent/exceptions_helper.cc b/test/ti-agent/exceptions_helper.cc new file mode 100644 index 0000000000..74f7ecc881 --- /dev/null +++ b/test/ti-agent/exceptions_helper.cc @@ -0,0 +1,198 @@ +/* + * 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_exceptions { + +struct ExceptionsData { + jclass test_klass; + jclass exception_klass; + jmethodID exception_event; + jmethodID exception_catch_event; +}; + +static void exceptionCB(jvmtiEnv* jvmti, + JNIEnv* jnienv, + jthread thread, + jmethodID throw_method, + jlocation throw_location, + jobject throwable, + jmethodID catch_method, + jlocation catch_location) { + ExceptionsData* data = nullptr; + if (JvmtiErrorToException(jnienv, jvmti, + jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { + return; + } + DCHECK(throwable != nullptr); + if (!jnienv->IsInstanceOf(throwable, data->exception_klass)) { + return; + } + jthrowable e = jnienv->ExceptionOccurred(); + jnienv->ExceptionClear(); + CHECK(data->exception_event != nullptr); + jobject throw_method_arg = GetJavaMethod(jvmti, jnienv, throw_method); + jobject catch_method_arg = + catch_method != nullptr ? GetJavaMethod(jvmti, jnienv, catch_method) : nullptr; + jnienv->CallStaticVoidMethod(data->test_klass, + data->exception_event, + thread, + throw_method_arg, + static_cast<jlong>(throw_location), + throwable, + catch_method_arg, + static_cast<jlong>(catch_location)); + jnienv->DeleteLocalRef(throw_method_arg); + if (catch_method_arg != nullptr) { + jnienv->DeleteLocalRef(catch_method_arg); + } + if (e != nullptr) { + jnienv->Throw(e); + } +} + + +static void exceptionCatchCB(jvmtiEnv* jvmti, + JNIEnv* jnienv, + jthread thread, + jmethodID catch_method, + jlocation catch_location, + jobject throwable) { + ExceptionsData* data = nullptr; + if (JvmtiErrorToException(jnienv, jvmti, + jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { + return; + } + if (!jnienv->IsSameObject(data->exception_klass, jnienv->GetObjectClass(throwable))) { + return; + } + jthrowable e = jnienv->ExceptionOccurred(); + jnienv->ExceptionClear(); + CHECK(data->exception_catch_event != nullptr); + jobject catch_method_arg = GetJavaMethod(jvmti, jnienv, catch_method); + jnienv->CallStaticVoidMethod(data->test_klass, + data->exception_catch_event, + thread, + catch_method_arg, + static_cast<jlong>(catch_location), + throwable); + jnienv->DeleteLocalRef(catch_method_arg); + if (e != nullptr) { + jnienv->Throw(e); + } +} + +extern "C" JNIEXPORT void JNICALL Java_art_Exceptions_setupExceptionTracing( + JNIEnv* env, + jclass exception ATTRIBUTE_UNUSED, + jclass klass, + jclass except, + jobject exception_event, + jobject exception_catch_event) { + ExceptionsData* data = nullptr; + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->Allocate(sizeof(ExceptionsData), + reinterpret_cast<unsigned char**>(&data)))) { + return; + } + jvmtiCapabilities caps; + memset(&caps, 0, sizeof(caps)); + caps.can_generate_exception_events = 1; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps))) { + return; + } + memset(data, 0, sizeof(ExceptionsData)); + data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(klass)); + data->exception_klass = reinterpret_cast<jclass>(env->NewGlobalRef(except)); + data->exception_event = + exception_event != nullptr ? env->FromReflectedMethod(exception_event) : nullptr; + data->exception_catch_event = + exception_catch_event != nullptr ? env->FromReflectedMethod(exception_catch_event) : nullptr; + + ExceptionsData* old_data = nullptr; + if (JvmtiErrorToException(env, jvmti_env, + jvmti_env->GetEnvironmentLocalStorage( + reinterpret_cast<void**>(&old_data)))) { + return; + } else if (old_data != nullptr && old_data->test_klass != 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; + } + + jvmtiEventCallbacks cb; + memset(&cb, 0, sizeof(cb)); + cb.Exception = exceptionCB; + cb.ExceptionCatch = exceptionCatchCB; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) { + return; + } +} + +extern "C" JNIEXPORT void JNICALL Java_art_Exceptions_enableExceptionCatchEvent( + JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) { + JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, + JVMTI_EVENT_EXCEPTION_CATCH, + thr)); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Exceptions_enableExceptionEvent( + JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) { + JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, + JVMTI_EVENT_EXCEPTION, + thr)); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Exceptions_disableExceptionCatchEvent( + JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) { + JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, + JVMTI_EVENT_EXCEPTION_CATCH, + thr)); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Exceptions_disableExceptionEvent( + JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) { + JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, + JVMTI_EVENT_EXCEPTION, + thr)); +} + +} // namespace common_exceptions + + +} // namespace art diff --git a/test/ti-agent/jvmti_helper.cc b/test/ti-agent/jvmti_helper.cc index 7280102c6f..c290e9b1ae 100644 --- a/test/ti-agent/jvmti_helper.cc +++ b/test/ti-agent/jvmti_helper.cc @@ -50,7 +50,7 @@ static const jvmtiCapabilities standard_caps = { .can_get_synthetic_attribute = 1, .can_get_owned_monitor_info = 0, .can_get_current_contended_monitor = 0, - .can_get_monitor_info = 0, + .can_get_monitor_info = 1, .can_pop_frame = 0, .can_redefine_classes = 1, .can_signal_thread = 0, diff --git a/test/ti-agent/monitors_helper.cc b/test/ti-agent/monitors_helper.cc new file mode 100644 index 0000000000..7c28edecd2 --- /dev/null +++ b/test/ti-agent/monitors_helper.cc @@ -0,0 +1,62 @@ +/* + * 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 "jni.h" +#include "jvmti.h" +#include <vector> +#include "jvmti_helper.h" +#include "jni_helper.h" +#include "test_env.h" +#include "scoped_local_ref.h" +namespace art { +namespace common_monitors { + +extern "C" JNIEXPORT jobject JNICALL Java_art_Monitors_getObjectMonitorUsage( + JNIEnv* env, jclass, jobject obj) { + ScopedLocalRef<jclass> klass(env, env->FindClass("art/Monitors$MonitorUsage")); + if (env->ExceptionCheck()) { + return nullptr; + } + jmethodID constructor = env->GetMethodID( + klass.get(), + "<init>", + "(Ljava/lang/Object;Ljava/lang/Thread;I[Ljava/lang/Thread;[Ljava/lang/Thread;)V"); + if (env->ExceptionCheck()) { + return nullptr; + } + jvmtiMonitorUsage usage; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetObjectMonitorUsage(obj, &usage))) { + return nullptr; + } + jobjectArray wait = CreateObjectArray(env, usage.waiter_count, "java/lang/Thread", + [&](jint i) { return usage.waiters[i]; }); + if (env->ExceptionCheck()) { + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(usage.waiters)); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(usage.notify_waiters)); + return nullptr; + } + jobjectArray notify_wait = CreateObjectArray(env, usage.notify_waiter_count, "java/lang/Thread", + [&](jint i) { return usage.notify_waiters[i]; }); + if (env->ExceptionCheck()) { + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(usage.waiters)); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(usage.notify_waiters)); + return nullptr; + } + return env->NewObject(klass.get(), constructor, + obj, usage.owner, usage.entry_count, wait, notify_wait); +} + +} // namespace common_monitors +} // namespace art diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc index 5d7c2f3c60..6e29e36e82 100644 --- a/test/ti-stress/stress.cc +++ b/test/ti-stress/stress.cc @@ -28,15 +28,31 @@ #include "jvmti.h" #include "utils.h" +#pragma clang diagnostic push +// slicer defines its own CHECK. b/65422458 +#pragma push_macro("CHECK") +#undef CHECK + +// Slicer's headers have code that triggers these warnings. b/65298177 +#pragma clang diagnostic ignored "-Wunused-parameter" +#pragma clang diagnostic ignored "-Wsign-compare" +#include "code_ir.h" +#include "control_flow_graph.h" +#include "dex_ir.h" +#include "dex_ir_builder.h" +#include "instrumentation.h" +#include "reader.h" +#include "writer.h" + +#pragma pop_macro("CHECK") +#pragma clang diagnostic pop + namespace art { // Should we do a 'full_rewrite' with this test? static constexpr bool kDoFullRewrite = true; struct StressData { - std::string dexter_cmd; - std::string out_temp_dex; - std::string in_temp_dex; bool vm_class_loader_initialized; bool trace_stress; bool redefine_stress; @@ -44,51 +60,60 @@ struct StressData { bool step_stress; }; -static void WriteToFile(const std::string& fname, jint data_len, const unsigned char* data) { - std::ofstream file(fname, std::ios::binary | std::ios::out | std::ios::trunc); - file.write(reinterpret_cast<const char*>(data), data_len); - file.flush(); -} - -static bool ReadIntoBuffer(const std::string& fname, /*out*/std::vector<unsigned char>* data) { - std::ifstream file(fname, std::ios::binary | std::ios::in); - file.seekg(0, std::ios::end); - size_t len = file.tellg(); - data->resize(len); - file.seekg(0); - file.read(reinterpret_cast<char*>(data->data()), len); - return len != 0; -} - -// TODO rewrite later. -static bool DoExtractClassFromData(StressData* data, - const std::string& class_name, +static bool DoExtractClassFromData(jvmtiEnv* env, + const std::string& descriptor, jint in_len, const unsigned char* in_data, - /*out*/std::vector<unsigned char>* dex) { - // Write the dex file into a temporary file. - WriteToFile(data->in_temp_dex, in_len, in_data); - // Clear out file so even if something suppresses the exit value we will still detect dexter - // failure. - WriteToFile(data->out_temp_dex, 0, nullptr); - // Have dexter do the extraction. - std::vector<std::string> args; - args.push_back(data->dexter_cmd); + /*out*/jint* out_len, + /*out*/unsigned char** out_data) { + dex::Reader reader(in_data, in_len); + dex::u4 class_idx = reader.FindClassIndex(descriptor.c_str()); + if (class_idx != dex::kNoIndex) { + reader.CreateClassIr(class_idx); + } else { + LOG(ERROR) << "ERROR: Can't find class " << descriptor; + return false; + } + auto dex_ir = reader.GetIr(); + if (kDoFullRewrite) { - args.push_back("-x"); - args.push_back("full_rewrite"); - } - args.push_back("-e"); - args.push_back(class_name); - args.push_back("-o"); - args.push_back(data->out_temp_dex); - args.push_back(data->in_temp_dex); - std::string error; - if (ExecAndReturnCode(args, &error) != 0) { - LOG(ERROR) << "unable to execute dexter: " << error; + for (auto& ir_method : dex_ir->encoded_methods) { + if (ir_method->code != nullptr) { + lir::CodeIr code_ir(ir_method.get(), dex_ir); + lir::ControlFlowGraph cfg_compact(&code_ir, false); + lir::ControlFlowGraph cfg_verbose(&code_ir, true); + code_ir.Assemble(); + } + } + } + dex::Writer writer(dex_ir); + + struct Allocator : public dex::Writer::Allocator { + explicit Allocator(jvmtiEnv* jvmti_env) : jvmti_env_(jvmti_env) {} + virtual void* Allocate(size_t size) { + unsigned char* out = nullptr; + if (JVMTI_ERROR_NONE != jvmti_env_->Allocate(size, &out)) { + return nullptr; + } else { + return out; + } + } + virtual void Free(void* ptr) { + jvmti_env_->Deallocate(reinterpret_cast<unsigned char*>(ptr)); + } + private: + jvmtiEnv* jvmti_env_; + }; + Allocator alloc(env); + size_t res_len; + unsigned char* res = writer.CreateImage(&alloc, &res_len); + if (res != nullptr) { + *out_data = res; + *out_len = res_len; + return true; + } else { return false; } - return ReadIntoBuffer(data->out_temp_dex, dex); } class ScopedThreadInfo { @@ -615,10 +640,10 @@ void JNICALL ClassFileLoadHookSecretNoOp(jvmtiEnv* jvmti, jint* new_class_data_len, unsigned char** new_class_data) { std::vector<unsigned char> out; - std::string name_str(name); - // Make the jvmti semi-descriptor into the java style descriptor (though with $ for inner - // classes). - std::replace(name_str.begin(), name_str.end(), '/', '.'); + // Make the jvmti semi-descriptor into the full descriptor. + std::string name_str("L"); + name_str += name; + name_str += ";"; StressData* data = nullptr; CHECK_EQ(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)), JVMTI_ERROR_NONE); @@ -626,15 +651,11 @@ void JNICALL ClassFileLoadHookSecretNoOp(jvmtiEnv* jvmti, LOG(WARNING) << "Ignoring load of class " << name << " because VMClassLoader is not yet " << "initialized. Transforming this class could cause spurious test failures."; return; - } else if (DoExtractClassFromData(data, name_str, class_data_len, class_data, /*out*/ &out)) { + } else if (DoExtractClassFromData(jvmti, name_str, class_data_len, class_data, + /*out*/ new_class_data_len, /*out*/ new_class_data)) { LOG(INFO) << "Extracted class: " << name; - unsigned char* new_data; - CHECK_EQ(JVMTI_ERROR_NONE, jvmti->Allocate(out.size(), &new_data)); - memcpy(new_data, out.data(), out.size()); - *new_class_data_len = static_cast<jint>(out.size()); - *new_class_data = new_data; } else { - std::cerr << "Unable to extract class " << name_str << std::endl; + std::cerr << "Unable to extract class " << name << std::endl; *new_class_data_len = 0; *new_class_data = nullptr; } @@ -653,7 +674,7 @@ static std::string GetOption(const std::string& in) { } // Options are -// jvmti-stress,[redefine,${DEXTER_BINARY},${TEMP_FILE_1},${TEMP_FILE_2},][trace,][field] +// jvmti-stress,[redefine,][trace,][field] static void ReadOptions(StressData* data, char* options) { std::string ops(options); CHECK_EQ(GetOption(ops), "jvmti-stress") << "Options should start with jvmti-stress"; @@ -668,12 +689,6 @@ static void ReadOptions(StressData* data, char* options) { data->field_stress = true; } else if (cur == "redefine") { data->redefine_stress = true; - ops = AdvanceOption(ops); - data->dexter_cmd = GetOption(ops); - ops = AdvanceOption(ops); - data->in_temp_dex = GetOption(ops); - ops = AdvanceOption(ops); - data->out_temp_dex = GetOption(ops); } else { LOG(FATAL) << "Unknown option: " << GetOption(ops); } diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt index 5806b6107f..d27b8fc06c 100644 --- a/tools/libcore_gcstress_debug_failures.txt +++ b/tools/libcore_gcstress_debug_failures.txt @@ -11,9 +11,11 @@ names: ["jsr166.CompletableFutureTest#testCompleteOnTimeout_completed", "libcore.icu.TransliteratorTest#testAll", "libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045", + "libcore.icu.RelativeDateTimeFormatterTest#test_bug25883157", "libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndTimeout", "libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndNoTimeout", "libcore.java.util.TimeZoneTest#testSetDefaultDeadlock", + "libcore.javax.crypto.CipherBasicsTest#testBasicEncryption", "org.apache.harmony.tests.java.util.TimerTest#testThrowingTaskKillsTimerThread"] } ] |