Merge "OatFileAssistant: look at vdex file for IsDexOptNeeded"
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index 4c82506..f5a95fa 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -46,6 +46,9 @@
$(info Disabling ART_BUILD_HOST_DEBUG)
endif
+# Enable the read barrier by default.
+ART_USE_READ_BARRIER ?= true
+
ART_CPP_EXTENSION := .cc
ifndef LIBART_IMG_HOST_BASE_ADDRESS
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 5bdfbc7..c87075f 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -87,7 +87,7 @@
ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
ART_GTEST_atomic_method_ref_map_test_DEX_DEPS := Interfaces
-ART_GTEST_class_linker_test_DEX_DEPS := ErroneousA ErroneousB Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
+ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
ART_GTEST_class_table_test_DEX_DEPS := XandY
ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes
@@ -107,6 +107,7 @@
ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods
ART_GTEST_profile_assistant_test_DEX_DEPS := ProfileTestMultiDex
ART_GTEST_profile_compilation_info_test_DEX_DEPS := ProfileTestMultiDex
+ART_GTEST_runtime_callbacks_test_DEX_DEPS := XandY
ART_GTEST_stub_test_DEX_DEPS := AllFields
ART_GTEST_transaction_test_DEX_DEPS := Transaction
ART_GTEST_type_lookup_table_test_DEX_DEPS := Lookup
@@ -120,14 +121,14 @@
ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \
$(HOST_CORE_IMAGE_optimizing_pic_64) \
$(HOST_CORE_IMAGE_optimizing_pic_32) \
- $(HOST_CORE_IMAGE_optimizing_no-pic_64) \
- $(HOST_CORE_IMAGE_optimizing_no-pic_32) \
+ $(HOST_CORE_IMAGE_interpreter_pic_64) \
+ $(HOST_CORE_IMAGE_interpreter_pic_32) \
$(HOST_OUT_EXECUTABLES)/patchoatd
ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \
$(TARGET_CORE_IMAGE_optimizing_pic_64) \
$(TARGET_CORE_IMAGE_optimizing_pic_32) \
- $(TARGET_CORE_IMAGE_optimizing_no-pic_64) \
- $(TARGET_CORE_IMAGE_optimizing_no-pic_32) \
+ $(TARGET_CORE_IMAGE_interpreter_pic_64) \
+ $(TARGET_CORE_IMAGE_interpreter_pic_32) \
$(TARGET_OUT_EXECUTABLES)/patchoatd
ART_GTEST_oat_file_assistant_test_HOST_DEPS := \
diff --git a/build/art.go b/build/art.go
index e6e0544..84269c3 100644
--- a/build/art.go
+++ b/build/art.go
@@ -58,7 +58,7 @@
asflags = append(asflags, "-DART_HEAP_POISONING=1")
}
- if envTrue(ctx, "ART_USE_READ_BARRIER") || ctx.AConfig().ArtUseReadBarrier() {
+ if !envFalse(ctx, "ART_USE_READ_BARRIER") || ctx.AConfig().ArtUseReadBarrier() {
// Used to change the read barrier type. Valid values are BAKER, BROOKS, TABLELOOKUP.
// The default is BAKER.
barrierType := envDefault(ctx, "ART_READ_BARRIER_TYPE", "BAKER")
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index f4838c1..0d45a50 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -23,7 +23,7 @@
#include "common_runtime_test.h"
#include "compiler.h"
-#include "jit/offline_profiling_info.h"
+#include "jit/profile_compilation_info.h"
#include "oat_file.h"
namespace art {
diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h
index 3db7306..18a9165 100644
--- a/compiler/debug/elf_debug_line_writer.h
+++ b/compiler/debug/elf_debug_line_writer.h
@@ -53,7 +53,8 @@
// Write line table for given set of methods.
// Returns the number of bytes written.
size_t WriteCompilationUnit(ElfCompilationUnit& compilation_unit) {
- const bool is64bit = Is64BitInstructionSet(builder_->GetIsa());
+ const InstructionSet isa = builder_->GetIsa();
+ const bool is64bit = Is64BitInstructionSet(isa);
const Elf_Addr base_address = compilation_unit.is_code_address_text_relative
? builder_->GetText()->GetAddress()
: 0;
@@ -66,7 +67,7 @@
std::unordered_map<std::string, size_t> directories_map;
int code_factor_bits_ = 0;
int dwarf_isa = -1;
- switch (builder_->GetIsa()) {
+ switch (isa) {
case kArm: // arm actually means thumb2.
case kThumb2:
code_factor_bits_ = 1; // 16-bit instuctions
@@ -103,7 +104,7 @@
for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(encoding); s++) {
StackMap stack_map = code_info.GetStackMapAt(s, encoding);
DCHECK(stack_map.IsValid());
- const uint32_t pc = stack_map.GetNativePcOffset(encoding.stack_map_encoding);
+ const uint32_t pc = stack_map.GetNativePcOffset(encoding.stack_map_encoding, isa);
const int32_t dex = stack_map.GetDexPc(encoding.stack_map_encoding);
pc2dex_map.push_back({pc, dex});
if (stack_map.HasDexRegisterMap(encoding.stack_map_encoding)) {
diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h
index 9645643..bce5387 100644
--- a/compiler/debug/elf_debug_loc_writer.h
+++ b/compiler/debug/elf_debug_loc_writer.h
@@ -92,7 +92,8 @@
bool is64bitValue,
uint64_t compilation_unit_code_address,
uint32_t dex_pc_low,
- uint32_t dex_pc_high) {
+ uint32_t dex_pc_high,
+ InstructionSet isa) {
std::vector<VariableLocation> variable_locations;
// Get stack maps sorted by pc (they might not be sorted internally).
@@ -111,7 +112,7 @@
// The main reason for this is to save space by avoiding undefined gaps.
continue;
}
- const uint32_t pc_offset = stack_map.GetNativePcOffset(encoding.stack_map_encoding);
+ const uint32_t pc_offset = stack_map.GetNativePcOffset(encoding.stack_map_encoding, isa);
DCHECK_LE(pc_offset, method_info->code_size);
DCHECK_LE(compilation_unit_code_address, method_info->code_address);
const uint32_t low_pc = dchecked_integral_cast<uint32_t>(
@@ -196,7 +197,8 @@
is64bitValue,
compilation_unit_code_address,
dex_pc_low,
- dex_pc_high);
+ dex_pc_high,
+ isa);
// Write .debug_loc entries.
dwarf::Writer<> debug_loc(debug_loc_buffer);
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index faf8b41..c03ffca 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -284,7 +284,7 @@
verification_results_(verification_results),
compiler_(Compiler::Create(this, compiler_kind)),
compiler_kind_(compiler_kind),
- instruction_set_(instruction_set == kArm ? kThumb2: instruction_set),
+ instruction_set_(instruction_set == kArm ? kThumb2 : instruction_set),
instruction_set_features_(instruction_set_features),
requires_constructor_barrier_lock_("constructor barrier lock"),
compiled_classes_lock_("compiled classes lock"),
@@ -1251,7 +1251,7 @@
}
}
- // java.lang.Reference visitor for VisitReferences.
+ // java.lang.ref.Reference visitor for VisitReferences.
void operator()(ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED,
ObjPtr<mirror::Reference> ref ATTRIBUTE_UNUSED) const {}
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 6bfdd4d..503fe3a 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -33,7 +33,7 @@
#include "dex_file.h"
#include "dex_file_types.h"
#include "driver/compiled_method_storage.h"
-#include "jit/offline_profiling_info.h"
+#include "jit/profile_compilation_info.h"
#include "invoke_type.h"
#include "method_reference.h"
#include "mirror/class.h" // For mirror::Class::Status.
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index 12684c0..1e4ca16 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -32,7 +32,7 @@
#include "mirror/object_array-inl.h"
#include "mirror/object-inl.h"
#include "handle_scope-inl.h"
-#include "jit/offline_profiling_info.h"
+#include "jit/profile_compilation_info.h"
#include "scoped_thread_state_change-inl.h"
namespace art {
diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc
index f9e5cb9..eac46e5 100644
--- a/compiler/exception_test.cc
+++ b/compiler/exception_test.cc
@@ -61,7 +61,7 @@
ArenaPool pool;
ArenaAllocator allocator(&pool);
- StackMapStream stack_maps(&allocator);
+ StackMapStream stack_maps(&allocator, kRuntimeISA);
stack_maps.BeginStackMapEntry(/* dex_pc */ 3u,
/* native_pc_offset */ 3u,
/* register_mask */ 0u,
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 4109345..459aca3 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -1166,9 +1166,9 @@
// belongs.
oat_index = GetOatIndexForDexCache(dex_cache);
ImageInfo& image_info = GetImageInfo(oat_index);
- {
- // Note: This table is only accessed from the image writer, avoid locking to prevent lock
- // order violations from root visiting.
+ if (!compile_app_image_) {
+ // Note: Avoid locking to prevent lock order violations from root visiting;
+ // image_info.class_table_ is only accessed from the image writer.
image_info.class_table_->InsertWithoutLocks(as_klass);
}
for (LengthPrefixedArray<ArtField>* cur_fields : fields) {
@@ -1265,7 +1265,14 @@
// class loader.
mirror::ClassLoader* class_loader = obj->AsClassLoader();
if (class_loader->GetClassTable() != nullptr) {
+ DCHECK(compile_app_image_);
+ DCHECK(class_loaders_.empty());
class_loaders_.insert(class_loader);
+ ImageInfo& image_info = GetImageInfo(oat_index);
+ // Note: Avoid locking to prevent lock order violations from root visiting;
+ // image_info.class_table_ table is only accessed from the image writer
+ // and class_loader->GetClassTable() is iterated but not modified.
+ image_info.class_table_->CopyWithoutLocks(*class_loader->GetClassTable());
}
}
AssignImageBinSlot(obj, oat_index);
diff --git a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc
index 8ca0ffe..ba654f4 100644
--- a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc
+++ b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc
@@ -160,7 +160,7 @@
while (HasNext()) {
ManagedRegister in_reg = CurrentParamRegister();
if (!in_reg.IsNoRegister()) {
- int32_t size = IsParamALongOrDouble(itr_args_)? 8 : 4;
+ int32_t size = IsParamALongOrDouble(itr_args_) ? 8 : 4;
int32_t spill_offset = CurrentParamStackOffset().Uint32Value();
ManagedRegisterSpill spill(in_reg, size, spill_offset);
entry_spills_.push_back(spill);
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 70c2738..99427f0 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -839,8 +839,8 @@
// last emitted is different than the native pc of the stack map just emitted.
size_t number_of_stack_maps = stack_map_stream_.GetNumberOfStackMaps();
if (number_of_stack_maps > 1) {
- DCHECK_NE(stack_map_stream_.GetStackMap(number_of_stack_maps - 1).native_pc_offset,
- stack_map_stream_.GetStackMap(number_of_stack_maps - 2).native_pc_offset);
+ DCHECK_NE(stack_map_stream_.GetStackMap(number_of_stack_maps - 1).native_pc_code_offset,
+ stack_map_stream_.GetStackMap(number_of_stack_maps - 2).native_pc_code_offset);
}
}
}
@@ -848,7 +848,8 @@
bool CodeGenerator::HasStackMapAtCurrentPc() {
uint32_t pc = GetAssembler()->CodeSize();
size_t count = stack_map_stream_.GetNumberOfStackMaps();
- return count > 0 && stack_map_stream_.GetStackMap(count - 1).native_pc_offset == pc;
+ CodeOffset native_pc_offset = stack_map_stream_.GetStackMap(count - 1).native_pc_code_offset;
+ return (count > 0) && (native_pc_offset.Uint32Value(GetInstructionSet()) == pc);
}
void CodeGenerator::MaybeRecordNativeDebugInfo(HInstruction* instruction,
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 38d532e..2d129af 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -608,7 +608,7 @@
number_of_register_pairs_(number_of_register_pairs),
core_callee_save_mask_(core_callee_save_mask),
fpu_callee_save_mask_(fpu_callee_save_mask),
- stack_map_stream_(graph->GetArena()),
+ stack_map_stream_(graph->GetArena(), graph->GetInstructionSet()),
block_order_(nullptr),
jit_string_roots_(StringReferenceValueComparator(),
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 9c9c604..f5b6ebe 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1239,7 +1239,8 @@
// Adjust native pc offsets in stack maps.
for (size_t i = 0, num = stack_map_stream_.GetNumberOfStackMaps(); i != num; ++i) {
- uint32_t old_position = stack_map_stream_.GetStackMap(i).native_pc_offset;
+ uint32_t old_position =
+ stack_map_stream_.GetStackMap(i).native_pc_code_offset.Uint32Value(kThumb2);
uint32_t new_position = __ GetAdjustedPosition(old_position);
stack_map_stream_.SetStackMapNativePcOffset(i, new_position);
}
@@ -3331,7 +3332,7 @@
InvokeRuntimeCallingConvention calling_convention;
locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
- // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but
+ // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
// we only need the former.
locations->SetOut(Location::RegisterLocation(R0));
}
@@ -3458,7 +3459,7 @@
InvokeRuntimeCallingConvention calling_convention;
locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
- // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but
+ // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
// we only need the latter.
locations->SetOut(Location::RegisterLocation(R1));
}
@@ -7150,18 +7151,7 @@
HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM::GetSupportedInvokeStaticOrDirectDispatch(
const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
- HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info;
- // We disable pc-relative load when there is an irreducible loop, as the optimization
- // is incompatible with it.
- // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods
- // with irreducible loops.
- if (GetGraph()->HasIrreducibleLoops() &&
- (dispatch_info.method_load_kind ==
- HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative)) {
- dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
- }
-
- return dispatch_info;
+ return desired_dispatch_info;
}
Register CodeGeneratorARM::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke,
@@ -7181,8 +7171,7 @@
// save one load. However, since this is just an intrinsic slow path we prefer this
// simple and more robust approach rather that trying to determine if that's the case.
SlowPathCode* slow_path = GetCurrentSlowPath();
- DCHECK(slow_path != nullptr); // For intrinsified invokes the call is emitted on the slow path.
- if (slow_path->IsCoreRegisterSaved(location.AsRegister<Register>())) {
+ if (slow_path != nullptr && slow_path->IsCoreRegisterSaved(location.AsRegister<Register>())) {
int stack_offset = slow_path->GetStackOffsetOfCoreRegister(location.AsRegister<Register>());
__ LoadFromOffset(kLoadWord, temp, SP, stack_offset);
return temp;
@@ -7190,7 +7179,8 @@
return location.AsRegister<Register>();
}
-void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
+Location CodeGeneratorARM::GenerateCalleeMethodStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
+ Location temp) {
Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
switch (invoke->GetMethodLoadKind()) {
case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
@@ -7239,6 +7229,11 @@
break;
}
}
+ return callee_method;
+}
+
+void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
+ Location callee_method = GenerateCalleeMethodStaticOrDirectCall(invoke, temp);
switch (invoke->GetCodePtrLocation()) {
case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 52d1857..df2dbc7 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -456,6 +456,7 @@
const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
HInvokeStaticOrDirect* invoke) OVERRIDE;
+ Location GenerateCalleeMethodStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE;
void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE;
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 68d0b86..9762ee8 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -3993,7 +3993,8 @@
return desired_dispatch_info;
}
-void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
+Location CodeGeneratorARM64::GenerateCalleeMethodStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
+ Location temp) {
// Make sure that ArtMethod* is passed in kArtMethodRegister as per the calling convention.
Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
switch (invoke->GetMethodLoadKind()) {
@@ -4047,6 +4048,12 @@
break;
}
}
+ return callee_method;
+}
+
+void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
+ // All registers are assumed to be correctly set up.
+ Location callee_method = GenerateCalleeMethodStaticOrDirectCall(invoke, temp);
switch (invoke->GetCodePtrLocation()) {
case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
@@ -4655,7 +4662,7 @@
}
void InstructionCodeGeneratorARM64::VisitMonitorOperation(HMonitorOperation* instruction) {
- codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject: kQuickUnlockObject,
+ codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject : kQuickUnlockObject,
instruction,
instruction->GetDexPc());
if (instruction->IsEnter()) {
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index a9dca92..7d3c655 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -527,6 +527,7 @@
const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
HInvokeStaticOrDirect* invoke) OVERRIDE;
+ Location GenerateCalleeMethodStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE;
void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE;
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 592ee5a..ffaf18f 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -3336,7 +3336,7 @@
InvokeRuntimeCallingConventionARMVIXL calling_convention;
locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
- // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but
+ // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
// we only need the former.
locations->SetOut(LocationFrom(r0));
}
@@ -3450,7 +3450,7 @@
InvokeRuntimeCallingConventionARMVIXL calling_convention;
locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
- // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but
+ // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
// we only need the latter.
locations->SetOut(LocationFrom(r1));
}
@@ -7233,18 +7233,7 @@
HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch(
const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
- HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info;
- // We disable pc-relative load when there is an irreducible loop, as the optimization
- // is incompatible with it.
- // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods
- // with irreducible loops.
- if (GetGraph()->HasIrreducibleLoops() &&
- (dispatch_info.method_load_kind ==
- HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative)) {
- dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
- }
-
- return dispatch_info;
+ return desired_dispatch_info;
}
vixl32::Register CodeGeneratorARMVIXL::GetInvokeStaticOrDirectExtraParameter(
@@ -7273,7 +7262,7 @@
return RegisterFrom(location);
}
-void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall(
+Location CodeGeneratorARMVIXL::GenerateCalleeMethodStaticOrDirectCall(
HInvokeStaticOrDirect* invoke, Location temp) {
Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
switch (invoke->GetMethodLoadKind()) {
@@ -7324,6 +7313,12 @@
break;
}
}
+ return callee_method;
+}
+
+void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
+ Location temp) {
+ Location callee_method = GenerateCalleeMethodStaticOrDirectCall(invoke, temp);
switch (invoke->GetCodePtrLocation()) {
case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index be65353..8ae3b7d 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -537,6 +537,7 @@
const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
HInvokeStaticOrDirect* invoke) OVERRIDE;
+ Location GenerateCalleeMethodStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE;
void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE;
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index a038382..76be74e 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -496,7 +496,8 @@
// Adjust native pc offsets in stack maps.
for (size_t i = 0, num = stack_map_stream_.GetNumberOfStackMaps(); i != num; ++i) {
- uint32_t old_position = stack_map_stream_.GetStackMap(i).native_pc_offset;
+ uint32_t old_position =
+ stack_map_stream_.GetStackMap(i).native_pc_code_offset.Uint32Value(kMips);
uint32_t new_position = __ GetAdjustedPosition(old_position);
DCHECK_GE(new_position, old_position);
stack_map_stream_.SetStackMapNativePcOffset(i, new_position);
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 446dea6..192b4a5 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -450,7 +450,8 @@
// Adjust native pc offsets in stack maps.
for (size_t i = 0, num = stack_map_stream_.GetNumberOfStackMaps(); i != num; ++i) {
- uint32_t old_position = stack_map_stream_.GetStackMap(i).native_pc_offset;
+ uint32_t old_position =
+ stack_map_stream_.GetStackMap(i).native_pc_code_offset.Uint32Value(kMips64);
uint32_t new_position = __ GetAdjustedPosition(old_position);
DCHECK_GE(new_position, old_position);
stack_map_stream_.SetStackMapNativePcOffset(i, new_position);
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 853c91f..5c561f5 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1778,7 +1778,7 @@
cond = X86Condition(condition->GetCondition());
}
} else {
- // Must be a boolean condition, which needs to be compared to 0.
+ // Must be a Boolean condition, which needs to be compared to 0.
Register cond_reg = locations->InAt(2).AsRegister<Register>();
__ testl(cond_reg, cond_reg);
}
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 74c71cc..c4caf4b 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -1809,7 +1809,7 @@
cond = X86_64IntegerCondition(condition->GetCondition());
}
} else {
- // Must be a boolean condition, which needs to be compared to 0.
+ // Must be a Boolean condition, which needs to be compared to 0.
CpuRegister cond_reg = locations->InAt(2).AsRegister<CpuRegister>();
__ testl(cond_reg, cond_reg);
}
@@ -4210,7 +4210,7 @@
void CodeGeneratorX86_64::GenerateMemoryBarrier(MemBarrierKind kind) {
/*
- * According to the JSR-133 Cookbook, for x86 only StoreLoad/AnyAny barriers need memory fence.
+ * According to the JSR-133 Cookbook, for x86-64 only StoreLoad/AnyAny barriers need memory fence.
* All other barriers (LoadAny, AnyStore, StoreStore) are nops due to the x86-64 memory model.
* For those cases, all we need to ensure is that there is a scheduling barrier in place.
*/
diff --git a/compiler/optimizing/dex_cache_array_fixups_arm.cc b/compiler/optimizing/dex_cache_array_fixups_arm.cc
index 9ddcd56..cfcb276 100644
--- a/compiler/optimizing/dex_cache_array_fixups_arm.cc
+++ b/compiler/optimizing/dex_cache_array_fixups_arm.cc
@@ -65,7 +65,7 @@
if (invoke->HasPcRelativeDexCache() &&
!IsCallFreeIntrinsic<IntrinsicLocationsBuilderARMType>(invoke, codegen_)) {
HArmDexCacheArraysBase* base =
- GetOrCreateDexCacheArrayBase(invoke->GetDexFileForPcRelativeDexCache());
+ GetOrCreateDexCacheArrayBase(invoke, invoke->GetDexFileForPcRelativeDexCache());
// Update the element offset in base.
DexCacheArraysLayout layout(kArmPointerSize, &invoke->GetDexFileForPcRelativeDexCache());
base->UpdateElementOffset(layout.MethodOffset(invoke->GetDexMethodIndex()));
@@ -75,21 +75,28 @@
}
}
- HArmDexCacheArraysBase* GetOrCreateDexCacheArrayBase(const DexFile& dex_file) {
- // Ensure we only initialize the pointer once for each dex file.
- auto lb = dex_cache_array_bases_.lower_bound(&dex_file);
- if (lb != dex_cache_array_bases_.end() &&
- !dex_cache_array_bases_.key_comp()(&dex_file, lb->first)) {
- return lb->second;
- }
+ HArmDexCacheArraysBase* GetOrCreateDexCacheArrayBase(HInstruction* cursor,
+ const DexFile& dex_file) {
+ if (GetGraph()->HasIrreducibleLoops()) {
+ HArmDexCacheArraysBase* base = new (GetGraph()->GetArena()) HArmDexCacheArraysBase(dex_file);
+ cursor->GetBlock()->InsertInstructionBefore(base, cursor);
+ return base;
+ } else {
+ // Ensure we only initialize the pointer once for each dex file.
+ auto lb = dex_cache_array_bases_.lower_bound(&dex_file);
+ if (lb != dex_cache_array_bases_.end() &&
+ !dex_cache_array_bases_.key_comp()(&dex_file, lb->first)) {
+ return lb->second;
+ }
- // Insert the base at the start of the entry block, move it to a better
- // position later in MoveBaseIfNeeded().
- HArmDexCacheArraysBase* base = new (GetGraph()->GetArena()) HArmDexCacheArraysBase(dex_file);
- HBasicBlock* entry_block = GetGraph()->GetEntryBlock();
- entry_block->InsertInstructionBefore(base, entry_block->GetFirstInstruction());
- dex_cache_array_bases_.PutBefore(lb, &dex_file, base);
- return base;
+ // Insert the base at the start of the entry block, move it to a better
+ // position later in MoveBaseIfNeeded().
+ HArmDexCacheArraysBase* base = new (GetGraph()->GetArena()) HArmDexCacheArraysBase(dex_file);
+ HBasicBlock* entry_block = GetGraph()->GetEntryBlock();
+ entry_block->InsertInstructionBefore(base, entry_block->GetFirstInstruction());
+ dex_cache_array_bases_.PutBefore(lb, &dex_file, base);
+ return base;
+ }
}
CodeGeneratorARMType* codegen_;
@@ -100,11 +107,6 @@
};
void DexCacheArrayFixups::Run() {
- if (graph_->HasIrreducibleLoops()) {
- // Do not run this optimization, as irreducible loops do not work with an instruction
- // that can be live-in at the irreducible loop header.
- return;
- }
DexCacheArrayFixupsVisitor visitor(graph_, codegen_);
visitor.VisitInsertionOrder();
visitor.MoveBasesIfNeeded();
diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc
index f5931a2..c93bc21 100644
--- a/compiler/optimizing/gvn.cc
+++ b/compiler/optimizing/gvn.cc
@@ -399,7 +399,7 @@
ArenaVector<ValueSet*> sets_;
// BitVector which serves as a fast-access map from block id to
- // visited/unvisited boolean.
+ // visited/unvisited Boolean.
ArenaBitVector visited_blocks_;
DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer);
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index ef8d74d..cac385c 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -1,4 +1,3 @@
-
/*
* Copyright (C) 2016 The Android Open Source Project
*
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 8f64fae..c262cf9 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -2592,6 +2592,58 @@
__ Lsr(out, out, 5);
}
+void IntrinsicLocationsBuilderARM::VisitReferenceGetReferent(HInvoke* invoke) {
+ if (kEmitCompilerReadBarrier) {
+ // Do not intrinsify this call with the read barrier configuration.
+ return;
+ }
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kCallOnSlowPath,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+ locations->AddTemp(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorARM::VisitReferenceGetReferent(HInvoke* invoke) {
+ DCHECK(!kEmitCompilerReadBarrier);
+ ArmAssembler* const assembler = GetAssembler();
+ LocationSummary* locations = invoke->GetLocations();
+
+ Register obj = locations->InAt(0).AsRegister<Register>();
+ Register out = locations->Out().AsRegister<Register>();
+
+ SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
+ codegen_->AddSlowPath(slow_path);
+
+ // Load ArtMethod first.
+ HInvokeStaticOrDirect* invoke_direct = invoke->AsInvokeStaticOrDirect();
+ DCHECK(invoke_direct != nullptr);
+ Register temp = codegen_->GenerateCalleeMethodStaticOrDirectCall(
+ invoke_direct, locations->GetTemp(0)).AsRegister<Register>();
+
+ // Now get declaring class.
+ __ ldr(temp, Address(temp, ArtMethod::DeclaringClassOffset().Int32Value()));
+
+ uint32_t slow_path_flag_offset = codegen_->GetReferenceSlowFlagOffset();
+ uint32_t disable_flag_offset = codegen_->GetReferenceDisableFlagOffset();
+ DCHECK_NE(slow_path_flag_offset, 0u);
+ DCHECK_NE(disable_flag_offset, 0u);
+ DCHECK_NE(slow_path_flag_offset, disable_flag_offset);
+
+ // Check static flags that prevent using intrinsic.
+ __ ldr(IP, Address(temp, disable_flag_offset));
+ __ ldr(temp, Address(temp, slow_path_flag_offset));
+ __ orr(IP, IP, ShifterOperand(temp));
+ __ CompareAndBranchIfNonZero(IP, slow_path->GetEntryLabel());
+
+ // Fast path.
+ __ ldr(out, Address(obj, mirror::Reference::ReferentOffset().Int32Value()));
+ codegen_->MaybeRecordImplicitNullCheck(invoke);
+ __ MaybeUnpoisonHeapReference(out);
+ __ Bind(slow_path->GetExitLabel());
+}
+
UNIMPLEMENTED_INTRINSIC(ARM, MathMinDoubleDouble)
UNIMPLEMENTED_INTRINSIC(ARM, MathMinFloatFloat)
UNIMPLEMENTED_INTRINSIC(ARM, MathMaxDoubleDouble)
@@ -2605,7 +2657,6 @@
UNIMPLEMENTED_INTRINSIC(ARM, MathRoundFloat) // Could be done by changing rounding mode, maybe?
UNIMPLEMENTED_INTRINSIC(ARM, UnsafeCASLong) // High register pressure.
UNIMPLEMENTED_INTRINSIC(ARM, SystemArrayCopyChar)
-UNIMPLEMENTED_INTRINSIC(ARM, ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(ARM, IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(ARM, LongHighestOneBit)
UNIMPLEMENTED_INTRINSIC(ARM, IntegerLowestOneBit)
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index d8a896e..bbf826c 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -2773,7 +2773,65 @@
GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
}
-UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent)
+void IntrinsicLocationsBuilderARM64::VisitReferenceGetReferent(HInvoke* invoke) {
+ if (kEmitCompilerReadBarrier) {
+ // Do not intrinsify this call with the read barrier configuration.
+ return;
+ }
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kCallOnSlowPath,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+ locations->AddTemp(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorARM64::VisitReferenceGetReferent(HInvoke* invoke) {
+ DCHECK(!kEmitCompilerReadBarrier);
+ MacroAssembler* masm = GetVIXLAssembler();
+ LocationSummary* locations = invoke->GetLocations();
+
+ Register obj = InputRegisterAt(invoke, 0);
+ Register out = OutputRegister(invoke);
+
+ SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
+ codegen_->AddSlowPath(slow_path);
+
+ // Load ArtMethod first.
+ HInvokeStaticOrDirect* invoke_direct = invoke->AsInvokeStaticOrDirect();
+ DCHECK(invoke_direct != nullptr);
+ Register temp0 = XRegisterFrom(codegen_->GenerateCalleeMethodStaticOrDirectCall(
+ invoke_direct, locations->GetTemp(0)));
+
+ // Now get declaring class.
+ __ Ldr(temp0.W(), MemOperand(temp0, ArtMethod::DeclaringClassOffset().Int32Value()));
+
+ uint32_t slow_path_flag_offset = codegen_->GetReferenceSlowFlagOffset();
+ uint32_t disable_flag_offset = codegen_->GetReferenceDisableFlagOffset();
+ DCHECK_NE(slow_path_flag_offset, 0u);
+ DCHECK_NE(disable_flag_offset, 0u);
+ DCHECK_NE(slow_path_flag_offset, disable_flag_offset);
+
+ // Check static flags that prevent using intrinsic.
+ if (slow_path_flag_offset == disable_flag_offset + 1) {
+ // Load two adjacent flags in one 64-bit load.
+ __ Ldr(temp0, MemOperand(temp0, disable_flag_offset));
+ } else {
+ UseScratchRegisterScope temps(masm);
+ Register temp1 = temps.AcquireW();
+ __ Ldr(temp1.W(), MemOperand(temp0, disable_flag_offset));
+ __ Ldr(temp0.W(), MemOperand(temp0, slow_path_flag_offset));
+ __ Orr(temp0, temp1, temp0);
+ }
+ __ Cbnz(temp0, slow_path->GetEntryLabel());
+
+ // Fast path.
+ __ Ldr(out, HeapOperand(obj, mirror::Reference::ReferentOffset().Int32Value()));
+ codegen_->MaybeRecordImplicitNullCheck(invoke);
+ codegen_->GetAssembler()->MaybeUnpoisonHeapReference(out);
+ __ Bind(slow_path->GetExitLabel());
+}
+
UNIMPLEMENTED_INTRINSIC(ARM64, IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(ARM64, LongHighestOneBit)
UNIMPLEMENTED_INTRINSIC(ARM64, IntegerLowestOneBit)
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 85e84d8..68c2d2e 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -2688,6 +2688,60 @@
__ Lsr(out, out, 5);
}
+void IntrinsicLocationsBuilderARMVIXL::VisitReferenceGetReferent(HInvoke* invoke) {
+ if (kEmitCompilerReadBarrier) {
+ // Do not intrinsify this call with the read barrier configuration.
+ return;
+ }
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kCallOnSlowPath,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+ locations->AddTemp(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorARMVIXL::VisitReferenceGetReferent(HInvoke* invoke) {
+ DCHECK(!kEmitCompilerReadBarrier);
+ ArmVIXLAssembler* assembler = GetAssembler();
+ LocationSummary* locations = invoke->GetLocations();
+
+ vixl32::Register obj = InputRegisterAt(invoke, 0);
+ vixl32::Register out = OutputRegister(invoke);
+
+ SlowPathCodeARMVIXL* slow_path = new (GetAllocator()) IntrinsicSlowPathARMVIXL(invoke);
+ codegen_->AddSlowPath(slow_path);
+
+ // Load ArtMethod first.
+ HInvokeStaticOrDirect* invoke_direct = invoke->AsInvokeStaticOrDirect();
+ DCHECK(invoke_direct != nullptr);
+ vixl32::Register temp0 = RegisterFrom(codegen_->GenerateCalleeMethodStaticOrDirectCall(
+ invoke_direct, locations->GetTemp(0)));
+
+ // Now get declaring class.
+ __ Ldr(temp0, MemOperand(temp0, ArtMethod::DeclaringClassOffset().Int32Value()));
+
+ uint32_t slow_path_flag_offset = codegen_->GetReferenceSlowFlagOffset();
+ uint32_t disable_flag_offset = codegen_->GetReferenceDisableFlagOffset();
+ DCHECK_NE(slow_path_flag_offset, 0u);
+ DCHECK_NE(disable_flag_offset, 0u);
+ DCHECK_NE(slow_path_flag_offset, disable_flag_offset);
+
+ // Check static flags that prevent using intrinsic.
+ UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
+ vixl32::Register temp1 = temps.Acquire();
+ __ Ldr(temp1, MemOperand(temp0, disable_flag_offset));
+ __ Ldr(temp0, MemOperand(temp0, slow_path_flag_offset));
+ __ Orr(temp0, temp1, temp0);
+ __ CompareAndBranchIfNonZero(temp0, slow_path->GetEntryLabel());
+
+ // Fast path.
+ __ Ldr(out, MemOperand(obj, mirror::Reference::ReferentOffset().Int32Value()));
+ codegen_->MaybeRecordImplicitNullCheck(invoke);
+ assembler->MaybeUnpoisonHeapReference(out);
+ __ Bind(slow_path->GetExitLabel());
+}
+
UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMinDoubleDouble)
UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMinFloatFloat)
UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathMaxDoubleDouble)
@@ -2701,7 +2755,6 @@
UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRoundFloat) // Could be done by changing rounding mode, maybe?
UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeCASLong) // High register pressure.
UNIMPLEMENTED_INTRINSIC(ARMVIXL, SystemArrayCopyChar)
-UNIMPLEMENTED_INTRINSIC(ARMVIXL, ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(ARMVIXL, IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(ARMVIXL, LongHighestOneBit)
UNIMPLEMENTED_INTRINSIC(ARMVIXL, IntegerLowestOneBit)
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index f1ae549..6cf9b83 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1878,7 +1878,7 @@
// If we use 'value' directly, we would lose 'value'
// in the case that the store fails. Whether the
// store succeeds, or fails, it will load the
- // correct boolean value into the 'out' register.
+ // correct Boolean value into the 'out' register.
// This test isn't really necessary. We only support Primitive::kPrimInt,
// Primitive::kPrimNot, and we already verified that we're working on one
// of those two types. It's left here in case the code needs to support
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 3022e97..00a1fa1 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1477,7 +1477,7 @@
// If we use 'value' directly, we would lose 'value'
// in the case that the store fails. Whether the
// store succeeds, or fails, it will load the
- // correct boolean value into the 'out' register.
+ // correct Boolean value into the 'out' register.
if (type == Primitive::kPrimLong) {
__ Scd(out, TMP);
} else {
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index a2980dc..f0ea9e2 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -565,7 +565,7 @@
ArtMethod* GetArtMethod() const { return art_method_; }
void SetArtMethod(ArtMethod* method) { art_method_ = method; }
- // Returns an instruction with the opposite boolean value from 'cond'.
+ // Returns an instruction with the opposite Boolean value from 'cond'.
// The instruction has been inserted into the graph, either as a constant, or
// before cursor.
HInstruction* InsertOppositeCondition(HInstruction* cond, HInstruction* cursor);
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index 6087e36..a9a1e6f 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -31,7 +31,7 @@
DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry";
DCHECK_NE(dex_pc, static_cast<uint32_t>(-1)) << "invalid dex_pc";
current_entry_.dex_pc = dex_pc;
- current_entry_.native_pc_offset = native_pc_offset;
+ current_entry_.native_pc_code_offset = CodeOffset::FromOffset(native_pc_offset, instruction_set_);
current_entry_.register_mask = register_mask;
current_entry_.sp_mask = sp_mask;
current_entry_.num_dex_registers = num_dex_registers;
@@ -144,10 +144,10 @@
current_inline_info_ = InlineInfoEntry();
}
-uint32_t StackMapStream::ComputeMaxNativePcOffset() const {
- uint32_t max_native_pc_offset = 0u;
+CodeOffset StackMapStream::ComputeMaxNativePcCodeOffset() const {
+ CodeOffset max_native_pc_offset;
for (const StackMapEntry& entry : stack_maps_) {
- max_native_pc_offset = std::max(max_native_pc_offset, entry.native_pc_offset);
+ max_native_pc_offset = std::max(max_native_pc_offset, entry.native_pc_code_offset);
}
return max_native_pc_offset;
}
@@ -157,8 +157,9 @@
dex_register_maps_size_ = ComputeDexRegisterMapsSize();
ComputeInlineInfoEncoding(); // needs dex_register_maps_size_.
inline_info_size_ = inline_infos_.size() * inline_info_encoding_.GetEntrySize();
- uint32_t max_native_pc_offset = ComputeMaxNativePcOffset();
- size_t stack_map_size = stack_map_encoding_.SetFromSizes(max_native_pc_offset,
+ CodeOffset max_native_pc_offset = ComputeMaxNativePcCodeOffset();
+ // The stack map contains compressed native offsets.
+ size_t stack_map_size = stack_map_encoding_.SetFromSizes(max_native_pc_offset.CompressedValue(),
dex_pc_max_,
dex_register_maps_size_,
inline_info_size_,
@@ -319,7 +320,7 @@
StackMapEntry entry = stack_maps_[i];
stack_map.SetDexPc(stack_map_encoding_, entry.dex_pc);
- stack_map.SetNativePcOffset(stack_map_encoding_, entry.native_pc_offset);
+ stack_map.SetNativePcCodeOffset(stack_map_encoding_, entry.native_pc_code_offset);
stack_map.SetRegisterMask(stack_map_encoding_, entry.register_mask);
size_t number_of_stack_mask_bits = stack_map.GetNumberOfStackMaskBits(stack_map_encoding_);
if (entry.sp_mask != nullptr) {
@@ -546,7 +547,8 @@
StackMapEntry entry = stack_maps_[s];
// Check main stack map fields.
- DCHECK_EQ(stack_map.GetNativePcOffset(stack_map_encoding), entry.native_pc_offset);
+ DCHECK_EQ(stack_map.GetNativePcOffset(stack_map_encoding, instruction_set_),
+ entry.native_pc_code_offset.Uint32Value(instruction_set_));
DCHECK_EQ(stack_map.GetDexPc(stack_map_encoding), entry.dex_pc);
DCHECK_EQ(stack_map.GetRegisterMask(stack_map_encoding), entry.register_mask);
size_t num_stack_mask_bits = stack_map.GetNumberOfStackMaskBits(stack_map_encoding);
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index d6f42b3..8fec472 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -59,8 +59,10 @@
*/
class StackMapStream : public ValueObject {
public:
- explicit StackMapStream(ArenaAllocator* allocator)
+ explicit StackMapStream(ArenaAllocator* allocator,
+ InstructionSet instruction_set)
: allocator_(allocator),
+ instruction_set_(instruction_set),
stack_maps_(allocator->Adapter(kArenaAllocStackMapStream)),
location_catalog_entries_(allocator->Adapter(kArenaAllocStackMapStream)),
location_catalog_entries_indices_(allocator->Adapter(kArenaAllocStackMapStream)),
@@ -95,7 +97,7 @@
// See runtime/stack_map.h to know what these fields contain.
struct StackMapEntry {
uint32_t dex_pc;
- uint32_t native_pc_offset;
+ CodeOffset native_pc_code_offset;
uint32_t register_mask;
BitVector* sp_mask;
uint32_t num_dex_registers;
@@ -141,11 +143,9 @@
}
void SetStackMapNativePcOffset(size_t i, uint32_t native_pc_offset) {
- stack_maps_[i].native_pc_offset = native_pc_offset;
+ stack_maps_[i].native_pc_code_offset = CodeOffset::FromOffset(native_pc_offset, instruction_set_);
}
- uint32_t ComputeMaxNativePcOffset() const;
-
// Prepares the stream to fill in a memory region. Must be called before FillIn.
// Returns the size (in bytes) needed to store this stream.
size_t PrepareForFillIn();
@@ -158,6 +158,8 @@
size_t ComputeDexRegisterMapsSize() const;
void ComputeInlineInfoEncoding();
+ CodeOffset ComputeMaxNativePcCodeOffset() const;
+
// Returns the index of an entry with the same dex register map as the current_entry,
// or kNoSameDexMapFound if no such entry exists.
size_t FindEntryWithTheSameDexMap();
@@ -175,6 +177,7 @@
void CheckCodeInfo(MemoryRegion region) const;
ArenaAllocator* allocator_;
+ const InstructionSet instruction_set_;
ArenaVector<StackMapEntry> stack_maps_;
// A catalog of unique [location_kind, register_value] pairs (per method).
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index 22810ea..f68695b 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -47,7 +47,7 @@
TEST(StackMapTest, Test1) {
ArenaPool pool;
ArenaAllocator arena(&pool);
- StackMapStream stream(&arena);
+ StackMapStream stream(&arena, kRuntimeISA);
ArenaBitVector sp_mask(&arena, 0, false);
size_t number_of_dex_registers = 2;
@@ -78,7 +78,7 @@
ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding)));
ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding)));
ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map_encoding));
- ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map_encoding));
+ ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA));
ASSERT_EQ(0x3u, stack_map.GetRegisterMask(encoding.stack_map_encoding));
ASSERT_TRUE(CheckStackMask(stack_map, encoding.stack_map_encoding, sp_mask));
@@ -128,7 +128,7 @@
TEST(StackMapTest, Test2) {
ArenaPool pool;
ArenaAllocator arena(&pool);
- StackMapStream stream(&arena);
+ StackMapStream stream(&arena, kRuntimeISA);
ArtMethod art_method;
ArenaBitVector sp_mask1(&arena, 0, true);
@@ -193,7 +193,7 @@
ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding)));
ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding)));
ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map_encoding));
- ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map_encoding));
+ ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA));
ASSERT_EQ(0x3u, stack_map.GetRegisterMask(encoding.stack_map_encoding));
ASSERT_TRUE(CheckStackMask(stack_map, encoding.stack_map_encoding, sp_mask1));
@@ -252,7 +252,7 @@
ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1u, encoding)));
ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(128u, encoding)));
ASSERT_EQ(1u, stack_map.GetDexPc(encoding.stack_map_encoding));
- ASSERT_EQ(128u, stack_map.GetNativePcOffset(encoding.stack_map_encoding));
+ ASSERT_EQ(128u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA));
ASSERT_EQ(0xFFu, stack_map.GetRegisterMask(encoding.stack_map_encoding));
ASSERT_TRUE(CheckStackMask(stack_map, encoding.stack_map_encoding, sp_mask2));
@@ -306,7 +306,7 @@
ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(2u, encoding)));
ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(192u, encoding)));
ASSERT_EQ(2u, stack_map.GetDexPc(encoding.stack_map_encoding));
- ASSERT_EQ(192u, stack_map.GetNativePcOffset(encoding.stack_map_encoding));
+ ASSERT_EQ(192u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA));
ASSERT_EQ(0xABu, stack_map.GetRegisterMask(encoding.stack_map_encoding));
ASSERT_TRUE(CheckStackMask(stack_map, encoding.stack_map_encoding, sp_mask3));
@@ -360,7 +360,7 @@
ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(3u, encoding)));
ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(256u, encoding)));
ASSERT_EQ(3u, stack_map.GetDexPc(encoding.stack_map_encoding));
- ASSERT_EQ(256u, stack_map.GetNativePcOffset(encoding.stack_map_encoding));
+ ASSERT_EQ(256u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA));
ASSERT_EQ(0xCDu, stack_map.GetRegisterMask(encoding.stack_map_encoding));
ASSERT_TRUE(CheckStackMask(stack_map, encoding.stack_map_encoding, sp_mask4));
@@ -412,7 +412,7 @@
TEST(StackMapTest, TestNonLiveDexRegisters) {
ArenaPool pool;
ArenaAllocator arena(&pool);
- StackMapStream stream(&arena);
+ StackMapStream stream(&arena, kRuntimeISA);
ArenaBitVector sp_mask(&arena, 0, false);
uint32_t number_of_dex_registers = 2;
@@ -442,7 +442,7 @@
ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding)));
ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding)));
ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map_encoding));
- ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map_encoding));
+ ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA));
ASSERT_EQ(0x3u, stack_map.GetRegisterMask(encoding.stack_map_encoding));
ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map_encoding));
@@ -491,7 +491,7 @@
TEST(StackMapTest, DexRegisterMapOffsetOverflow) {
ArenaPool pool;
ArenaAllocator arena(&pool);
- StackMapStream stream(&arena);
+ StackMapStream stream(&arena, kRuntimeISA);
ArenaBitVector sp_mask(&arena, 0, false);
uint32_t number_of_dex_registers = 1024;
@@ -554,7 +554,7 @@
TEST(StackMapTest, TestShareDexRegisterMap) {
ArenaPool pool;
ArenaAllocator arena(&pool);
- StackMapStream stream(&arena);
+ StackMapStream stream(&arena, kRuntimeISA);
ArenaBitVector sp_mask(&arena, 0, false);
uint32_t number_of_dex_registers = 2;
@@ -612,7 +612,7 @@
TEST(StackMapTest, TestNoDexRegisterMap) {
ArenaPool pool;
ArenaAllocator arena(&pool);
- StackMapStream stream(&arena);
+ StackMapStream stream(&arena, kRuntimeISA);
ArenaBitVector sp_mask(&arena, 0, false);
uint32_t number_of_dex_registers = 0;
@@ -620,7 +620,7 @@
stream.EndStackMapEntry();
number_of_dex_registers = 1;
- stream.BeginStackMapEntry(1, 67, 0x4, &sp_mask, number_of_dex_registers, 0);
+ stream.BeginStackMapEntry(1, 68, 0x4, &sp_mask, number_of_dex_registers, 0);
stream.EndStackMapEntry();
size_t size = stream.PrepareForFillIn();
@@ -641,7 +641,7 @@
ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding)));
ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding)));
ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map_encoding));
- ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map_encoding));
+ ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA));
ASSERT_EQ(0x3u, stack_map.GetRegisterMask(encoding.stack_map_encoding));
ASSERT_FALSE(stack_map.HasDexRegisterMap(encoding.stack_map_encoding));
@@ -649,9 +649,9 @@
stack_map = code_info.GetStackMapAt(1, encoding);
ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1, encoding)));
- ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(67, encoding)));
+ ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(68, encoding)));
ASSERT_EQ(1u, stack_map.GetDexPc(encoding.stack_map_encoding));
- ASSERT_EQ(67u, stack_map.GetNativePcOffset(encoding.stack_map_encoding));
+ ASSERT_EQ(68u, stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA));
ASSERT_EQ(0x4u, stack_map.GetRegisterMask(encoding.stack_map_encoding));
ASSERT_FALSE(stack_map.HasDexRegisterMap(encoding.stack_map_encoding));
@@ -661,7 +661,7 @@
TEST(StackMapTest, InlineTest) {
ArenaPool pool;
ArenaAllocator arena(&pool);
- StackMapStream stream(&arena);
+ StackMapStream stream(&arena, kRuntimeISA);
ArtMethod art_method;
ArenaBitVector sp_mask1(&arena, 0, true);
@@ -823,4 +823,20 @@
}
}
+TEST(StackMapTest, CodeOffsetTest) {
+ // Test minimum alignments, encoding, and decoding.
+ CodeOffset offset_thumb2 = CodeOffset::FromOffset(kThumb2InstructionAlignment, kThumb2);
+ CodeOffset offset_arm64 = CodeOffset::FromOffset(kArm64InstructionAlignment, kArm64);
+ CodeOffset offset_x86 = CodeOffset::FromOffset(kX86InstructionAlignment, kX86);
+ CodeOffset offset_x86_64 = CodeOffset::FromOffset(kX86_64InstructionAlignment, kX86_64);
+ CodeOffset offset_mips = CodeOffset::FromOffset(kMipsInstructionAlignment, kMips);
+ CodeOffset offset_mips64 = CodeOffset::FromOffset(kMips64InstructionAlignment, kMips64);
+ EXPECT_EQ(offset_thumb2.Uint32Value(kThumb2), kThumb2InstructionAlignment);
+ EXPECT_EQ(offset_arm64.Uint32Value(kArm64), kArm64InstructionAlignment);
+ EXPECT_EQ(offset_x86.Uint32Value(kX86), kX86InstructionAlignment);
+ EXPECT_EQ(offset_x86_64.Uint32Value(kX86_64), kX86_64InstructionAlignment);
+ EXPECT_EQ(offset_mips.Uint32Value(kMips), kMipsInstructionAlignment);
+ EXPECT_EQ(offset_mips64.Uint32Value(kMips64), kMips64InstructionAlignment);
+}
+
} // namespace art
diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc
index f2cbebb..74b8f06 100644
--- a/compiler/utils/mips64/assembler_mips64_test.cc
+++ b/compiler/utils/mips64/assembler_mips64_test.cc
@@ -283,6 +283,38 @@
// FP Operations //
///////////////////
+TEST_F(AssemblerMIPS64Test, AddS) {
+ DriverStr(RepeatFFF(&mips64::Mips64Assembler::AddS, "add.s ${reg1}, ${reg2}, ${reg3}"), "add.s");
+}
+
+TEST_F(AssemblerMIPS64Test, AddD) {
+ DriverStr(RepeatFFF(&mips64::Mips64Assembler::AddD, "add.d ${reg1}, ${reg2}, ${reg3}"), "add.d");
+}
+
+TEST_F(AssemblerMIPS64Test, SubS) {
+ DriverStr(RepeatFFF(&mips64::Mips64Assembler::SubS, "sub.s ${reg1}, ${reg2}, ${reg3}"), "sub.s");
+}
+
+TEST_F(AssemblerMIPS64Test, SubD) {
+ DriverStr(RepeatFFF(&mips64::Mips64Assembler::SubD, "sub.d ${reg1}, ${reg2}, ${reg3}"), "sub.d");
+}
+
+TEST_F(AssemblerMIPS64Test, MulS) {
+ DriverStr(RepeatFFF(&mips64::Mips64Assembler::MulS, "mul.s ${reg1}, ${reg2}, ${reg3}"), "mul.s");
+}
+
+TEST_F(AssemblerMIPS64Test, MulD) {
+ DriverStr(RepeatFFF(&mips64::Mips64Assembler::MulD, "mul.d ${reg1}, ${reg2}, ${reg3}"), "mul.d");
+}
+
+TEST_F(AssemblerMIPS64Test, DivS) {
+ DriverStr(RepeatFFF(&mips64::Mips64Assembler::DivS, "div.s ${reg1}, ${reg2}, ${reg3}"), "div.s");
+}
+
+TEST_F(AssemblerMIPS64Test, DivD) {
+ DriverStr(RepeatFFF(&mips64::Mips64Assembler::DivD, "div.d ${reg1}, ${reg2}, ${reg3}"), "div.d");
+}
+
TEST_F(AssemblerMIPS64Test, SqrtS) {
DriverStr(RepeatFF(&mips64::Mips64Assembler::SqrtS, "sqrt.s ${reg1}, ${reg2}"), "sqrt.s");
}
@@ -567,6 +599,26 @@
DriverStr(RepeatRF(&mips64::Mips64Assembler::Dmtc1, "dmtc1 ${reg1}, ${reg2}"), "Dmtc1");
}
+TEST_F(AssemblerMIPS64Test, Lwc1) {
+ DriverStr(RepeatFRIb(&mips64::Mips64Assembler::Lwc1, -16, "lwc1 ${reg1}, {imm}(${reg2})"),
+ "lwc1");
+}
+
+TEST_F(AssemblerMIPS64Test, Ldc1) {
+ DriverStr(RepeatFRIb(&mips64::Mips64Assembler::Ldc1, -16, "ldc1 ${reg1}, {imm}(${reg2})"),
+ "ldc1");
+}
+
+TEST_F(AssemblerMIPS64Test, Swc1) {
+ DriverStr(RepeatFRIb(&mips64::Mips64Assembler::Swc1, -16, "swc1 ${reg1}, {imm}(${reg2})"),
+ "swc1");
+}
+
+TEST_F(AssemblerMIPS64Test, Sdc1) {
+ DriverStr(RepeatFRIb(&mips64::Mips64Assembler::Sdc1, -16, "sdc1 ${reg1}, {imm}(${reg2})"),
+ "sdc1");
+}
+
////////////////
// CALL / JMP //
////////////////
@@ -850,6 +902,16 @@
DriverStr(RepeatRIb(&mips64::Mips64Assembler::Ldpc, 18, code), "Ldpc");
}
+TEST_F(AssemblerMIPS64Test, Auipc) {
+ DriverStr(RepeatRIb(&mips64::Mips64Assembler::Auipc, 16, "auipc ${reg}, {imm}"), "Auipc");
+}
+
+TEST_F(AssemblerMIPS64Test, Addiupc) {
+ // The comment from the Lwpc() test applies to this Addiupc() test as well.
+ const char* code = ".set imm, {imm}\naddiupc ${reg}, (imm - ((imm & 0x40000) << 1)) << 2";
+ DriverStr(RepeatRIb(&mips64::Mips64Assembler::Addiupc, 19, code), "Addiupc");
+}
+
TEST_F(AssemblerMIPS64Test, LoadFarthestNearLabelAddress) {
mips64::Mips64Label label;
__ LoadLabelAddress(mips64::V0, &label);
@@ -1079,6 +1141,188 @@
EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (5 + kAdduCount) * 4);
}
+TEST_F(AssemblerMIPS64Test, Addu) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::Addu, "addu ${reg1}, ${reg2}, ${reg3}"), "addu");
+}
+
+TEST_F(AssemblerMIPS64Test, Addiu) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Addiu, -16, "addiu ${reg1}, ${reg2}, {imm}"),
+ "addiu");
+}
+
+TEST_F(AssemblerMIPS64Test, Daddu) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::Daddu, "daddu ${reg1}, ${reg2}, ${reg3}"), "daddu");
+}
+
+TEST_F(AssemblerMIPS64Test, Daddiu) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Daddiu, -16, "daddiu ${reg1}, ${reg2}, {imm}"),
+ "daddiu");
+}
+
+TEST_F(AssemblerMIPS64Test, Subu) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::Subu, "subu ${reg1}, ${reg2}, ${reg3}"), "subu");
+}
+
+TEST_F(AssemblerMIPS64Test, Dsubu) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::Dsubu, "dsubu ${reg1}, ${reg2}, ${reg3}"), "dsubu");
+}
+
+TEST_F(AssemblerMIPS64Test, MulR6) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::MulR6, "mul ${reg1}, ${reg2}, ${reg3}"), "mulR6");
+}
+
+TEST_F(AssemblerMIPS64Test, DivR6) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::DivR6, "div ${reg1}, ${reg2}, ${reg3}"), "divR6");
+}
+
+TEST_F(AssemblerMIPS64Test, ModR6) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::ModR6, "mod ${reg1}, ${reg2}, ${reg3}"), "modR6");
+}
+
+TEST_F(AssemblerMIPS64Test, DivuR6) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::DivuR6, "divu ${reg1}, ${reg2}, ${reg3}"),
+ "divuR6");
+}
+
+TEST_F(AssemblerMIPS64Test, ModuR6) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::ModuR6, "modu ${reg1}, ${reg2}, ${reg3}"),
+ "moduR6");
+}
+
+TEST_F(AssemblerMIPS64Test, Dmul) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::Dmul, "dmul ${reg1}, ${reg2}, ${reg3}"), "dmul");
+}
+
+TEST_F(AssemblerMIPS64Test, Ddiv) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::Ddiv, "ddiv ${reg1}, ${reg2}, ${reg3}"), "ddiv");
+}
+
+TEST_F(AssemblerMIPS64Test, Dmod) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::Dmod, "dmod ${reg1}, ${reg2}, ${reg3}"), "dmod");
+}
+
+TEST_F(AssemblerMIPS64Test, Ddivu) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::Ddivu, "ddivu ${reg1}, ${reg2}, ${reg3}"), "ddivu");
+}
+
+TEST_F(AssemblerMIPS64Test, Dmodu) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::Dmodu, "dmodu ${reg1}, ${reg2}, ${reg3}"), "dmodu");
+}
+
+TEST_F(AssemblerMIPS64Test, And) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::And, "and ${reg1}, ${reg2}, ${reg3}"), "and");
+}
+
+TEST_F(AssemblerMIPS64Test, Andi) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Andi, 16, "andi ${reg1}, ${reg2}, {imm}"), "andi");
+}
+
+TEST_F(AssemblerMIPS64Test, Or) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::Or, "or ${reg1}, ${reg2}, ${reg3}"), "or");
+}
+
+TEST_F(AssemblerMIPS64Test, Ori) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Ori, 16, "ori ${reg1}, ${reg2}, {imm}"), "ori");
+}
+
+TEST_F(AssemblerMIPS64Test, Xor) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::Xor, "xor ${reg1}, ${reg2}, ${reg3}"), "xor");
+}
+
+TEST_F(AssemblerMIPS64Test, Xori) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Xori, 16, "xori ${reg1}, ${reg2}, {imm}"), "xori");
+}
+
+TEST_F(AssemblerMIPS64Test, Nor) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::Nor, "nor ${reg1}, ${reg2}, ${reg3}"), "nor");
+}
+
+TEST_F(AssemblerMIPS64Test, Lb) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Lb, -16, "lb ${reg1}, {imm}(${reg2})"), "lb");
+}
+
+TEST_F(AssemblerMIPS64Test, Lh) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Lh, -16, "lh ${reg1}, {imm}(${reg2})"), "lh");
+}
+
+TEST_F(AssemblerMIPS64Test, Lw) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Lw, -16, "lw ${reg1}, {imm}(${reg2})"), "lw");
+}
+
+TEST_F(AssemblerMIPS64Test, Ld) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Ld, -16, "ld ${reg1}, {imm}(${reg2})"), "ld");
+}
+
+TEST_F(AssemblerMIPS64Test, Lbu) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Lbu, -16, "lbu ${reg1}, {imm}(${reg2})"), "lbu");
+}
+
+TEST_F(AssemblerMIPS64Test, Lhu) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Lhu, -16, "lhu ${reg1}, {imm}(${reg2})"), "lhu");
+}
+
+TEST_F(AssemblerMIPS64Test, Lwu) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Lwu, -16, "lwu ${reg1}, {imm}(${reg2})"), "lwu");
+}
+
+TEST_F(AssemblerMIPS64Test, Lui) {
+ DriverStr(RepeatRIb(&mips64::Mips64Assembler::Lui, 16, "lui ${reg}, {imm}"), "lui");
+}
+
+TEST_F(AssemblerMIPS64Test, Dahi) {
+ DriverStr(RepeatRIb(&mips64::Mips64Assembler::Dahi, 16, "dahi ${reg}, ${reg}, {imm}"), "dahi");
+}
+
+TEST_F(AssemblerMIPS64Test, Dati) {
+ DriverStr(RepeatRIb(&mips64::Mips64Assembler::Dati, 16, "dati ${reg}, ${reg}, {imm}"), "dati");
+}
+
+TEST_F(AssemblerMIPS64Test, Sb) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Sb, -16, "sb ${reg1}, {imm}(${reg2})"), "sb");
+}
+
+TEST_F(AssemblerMIPS64Test, Sh) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Sh, -16, "sh ${reg1}, {imm}(${reg2})"), "sh");
+}
+
+TEST_F(AssemblerMIPS64Test, Sw) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Sw, -16, "sw ${reg1}, {imm}(${reg2})"), "sw");
+}
+
+TEST_F(AssemblerMIPS64Test, Sd) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Sd, -16, "sd ${reg1}, {imm}(${reg2})"), "sd");
+}
+
+TEST_F(AssemblerMIPS64Test, Slt) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::Slt, "slt ${reg1}, ${reg2}, ${reg3}"), "slt");
+}
+
+TEST_F(AssemblerMIPS64Test, Sltu) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::Sltu, "sltu ${reg1}, ${reg2}, ${reg3}"), "sltu");
+}
+
+TEST_F(AssemblerMIPS64Test, Slti) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Slti, -16, "slti ${reg1}, ${reg2}, {imm}"),
+ "slti");
+}
+
+TEST_F(AssemblerMIPS64Test, Sltiu) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Sltiu, -16, "sltiu ${reg1}, ${reg2}, {imm}"),
+ "sltiu");
+}
+
+TEST_F(AssemblerMIPS64Test, Move) {
+ DriverStr(RepeatRR(&mips64::Mips64Assembler::Move, "or ${reg1}, ${reg2}, $zero"), "move");
+}
+
+TEST_F(AssemblerMIPS64Test, Clear) {
+ DriverStr(RepeatR(&mips64::Mips64Assembler::Clear, "or ${reg}, $zero, $zero"), "clear");
+}
+
+TEST_F(AssemblerMIPS64Test, Not) {
+ DriverStr(RepeatRR(&mips64::Mips64Assembler::Not, "nor ${reg1}, ${reg2}, $zero"), "not");
+}
+
TEST_F(AssemblerMIPS64Test, Bitswap) {
DriverStr(RepeatRR(&mips64::Mips64Assembler::Bitswap, "bitswap ${reg1}, ${reg2}"), "bitswap");
}
@@ -1230,6 +1474,18 @@
"dsra32");
}
+TEST_F(AssemblerMIPS64Test, Dsllv) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::Dsllv, "dsllv ${reg1}, ${reg2}, ${reg3}"), "dsllv");
+}
+
+TEST_F(AssemblerMIPS64Test, Dsrlv) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::Dsrlv, "dsrlv ${reg1}, ${reg2}, ${reg3}"), "dsrlv");
+}
+
+TEST_F(AssemblerMIPS64Test, Dsrav) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::Dsrav, "dsrav ${reg1}, ${reg2}, ${reg3}"), "dsrav");
+}
+
TEST_F(AssemblerMIPS64Test, Sc) {
DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Sc, -9, "sc ${reg1}, {imm}(${reg2})"), "sc");
}
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 4f06a91..5fc9972 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -1414,7 +1414,14 @@
ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
- {
+ // The two tests below make sure that fiddling with the method kind
+ // (static, virtual, interface) is detected by `ValidateDependencies`.
+
+ // An interface method lookup can succeed with a virtual method lookup on the same class.
+ // That's OK, as we only want to make sure there is a method being defined with the right
+ // flags. Therefore, polluting the interface methods with virtual methods does not have
+ // to fail verification.
+ if (resolution_kind != kVirtualMethodResolution) {
VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
bool found = false;
@@ -1433,7 +1440,8 @@
ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
- {
+ // See comment above that applies the same way.
+ if (resolution_kind != kInterfaceMethodResolution) {
VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
bool found = false;
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 3fbdb89..e8a92c1 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -64,7 +64,7 @@
#include "gc/space/space-inl.h"
#include "image_writer.h"
#include "interpreter/unstarted_runtime.h"
-#include "jit/offline_profiling_info.h"
+#include "jit/profile_compilation_info.h"
#include "leb128.h"
#include "linker/buffered_output_stream.h"
#include "linker/file_output_stream.h"
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index cdb3b9f..e86e560 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -30,7 +30,7 @@
#include "base/macros.h"
#include "dex_file-inl.h"
#include "dex2oat_environment_test.h"
-#include "jit/offline_profiling_info.h"
+#include "jit/profile_compilation_info.h"
#include "oat.h"
#include "oat_file.h"
#include "utils.h"
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index a2d1190..e2ee940 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -741,7 +741,7 @@
uint32_t GetAccessFlags() const { return access_flags_; }
const TypeId* Superclass() const { return superclass_; }
const TypeIdVector* Interfaces()
- { return interfaces_ == nullptr ? nullptr: interfaces_->GetTypeList(); }
+ { return interfaces_ == nullptr ? nullptr : interfaces_->GetTypeList(); }
uint32_t InterfacesOffset() { return interfaces_ == nullptr ? 0 : interfaces_->GetOffset(); }
const StringId* SourceFile() const { return source_file_; }
AnnotationsDirectoryItem* Annotations() const { return annotations_; }
diff --git a/dexlayout/dex_visualize.cc b/dexlayout/dex_visualize.cc
index 02274b2..75d47e4 100644
--- a/dexlayout/dex_visualize.cc
+++ b/dexlayout/dex_visualize.cc
@@ -31,7 +31,7 @@
#include "dex_ir.h"
#include "dexlayout.h"
-#include "jit/offline_profiling_info.h"
+#include "jit/profile_compilation_info.h"
namespace art {
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index cac6090..1add6bf 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -37,7 +37,7 @@
#include "dex_instruction-inl.h"
#include "dex_visualize.h"
#include "dex_writer.h"
-#include "jit/offline_profiling_info.h"
+#include "jit/profile_compilation_info.h"
#include "mem_map.h"
#include "os.h"
#include "utils.h"
diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc
index 5f8a118..ad599ae 100644
--- a/dexlayout/dexlayout_main.cc
+++ b/dexlayout/dexlayout_main.cc
@@ -30,7 +30,7 @@
#include <fcntl.h>
#include "base/logging.h"
-#include "jit/offline_profiling_info.h"
+#include "jit/profile_compilation_info.h"
#include "runtime.h"
#include "mem_map.h"
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 148ee88..69901c1 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -529,6 +529,12 @@
}
}
+ {
+ os << "OAT FILE STATS:\n";
+ VariableIndentationOutputStream vios(&os);
+ stats_.Dump(vios);
+ }
+
os << std::flush;
return success;
}
@@ -574,6 +580,116 @@
return nullptr;
}
+ struct Stats {
+ enum ByteKind {
+ kByteKindCode,
+ kByteKindQuickMethodHeader,
+ kByteKindCodeInfoLocationCatalog,
+ kByteKindCodeInfoDexRegisterMap,
+ kByteKindCodeInfoInlineInfo,
+ kByteKindCodeInfoEncoding,
+ kByteKindCodeInfoOther,
+ kByteKindStackMapNativePc,
+ kByteKindStackMapDexPc,
+ kByteKindStackMapDexRegisterMap,
+ kByteKindStackMapInlineInfo,
+ kByteKindStackMapRegisterMask,
+ kByteKindStackMapMask,
+ kByteKindStackMapOther,
+ kByteKindCount,
+ kByteKindStackMapFirst = kByteKindCodeInfoOther,
+ kByteKindStackMapLast = kByteKindStackMapOther,
+ };
+ int64_t bits[kByteKindCount] = {};
+ // Since code has deduplication, seen tracks already seen pointers to avoid double counting
+ // deduplicated code and tables.
+ std::unordered_set<const void*> seen;
+
+ // Returns true if it was newly added.
+ bool AddBitsIfUnique(ByteKind kind, int64_t count, const void* address) {
+ if (seen.insert(address).second == true) {
+ // True means the address was not already in the set.
+ AddBits(kind, count);
+ return true;
+ }
+ return false;
+ }
+
+ void AddBits(ByteKind kind, int64_t count) {
+ bits[kind] += count;
+ }
+
+ void Dump(VariableIndentationOutputStream& os) {
+ const int64_t sum = std::accumulate(bits, bits + kByteKindCount, 0u);
+ os.Stream() << "Dumping cumulative use of " << sum / kBitsPerByte << " accounted bytes\n";
+ if (sum > 0) {
+ const int64_t stack_map_bits = std::accumulate(bits + kByteKindStackMapFirst,
+ bits + kByteKindStackMapLast + 1,
+ 0u);
+ Dump(os, "Code ", bits[kByteKindCode], sum);
+ Dump(os, "QuickMethodHeader ", bits[kByteKindQuickMethodHeader], sum);
+ Dump(os, "CodeInfoEncoding ", bits[kByteKindCodeInfoEncoding], sum);
+ Dump(os, "CodeInfoLocationCatalog ", bits[kByteKindCodeInfoLocationCatalog], sum);
+ Dump(os, "CodeInfoDexRegisterMap ", bits[kByteKindCodeInfoDexRegisterMap], sum);
+ Dump(os, "CodeInfoInlineInfo ", bits[kByteKindCodeInfoInlineInfo], sum);
+ Dump(os, "CodeInfoStackMap ", stack_map_bits, sum);
+ {
+ ScopedIndentation indent1(&os);
+ Dump(os,
+ "StackMapNativePc ",
+ bits[kByteKindStackMapNativePc],
+ stack_map_bits,
+ "stack map");
+ Dump(os,
+ "StackMapDexPcEncoding ",
+ bits[kByteKindStackMapDexPc],
+ stack_map_bits,
+ "stack map");
+ Dump(os,
+ "StackMapDexRegisterMap ",
+ bits[kByteKindStackMapDexRegisterMap],
+ stack_map_bits,
+ "stack map");
+ Dump(os,
+ "StackMapInlineInfo ",
+ bits[kByteKindStackMapInlineInfo],
+ stack_map_bits,
+ "stack map");
+ Dump(os,
+ "StackMapRegisterMaskEncoding ",
+ bits[kByteKindStackMapRegisterMask],
+ stack_map_bits,
+ "stack map");
+ Dump(os,
+ "StackMapMask ",
+ bits[kByteKindStackMapMask],
+ stack_map_bits,
+ "stack map");
+ Dump(os,
+ "StackMapOther ",
+ bits[kByteKindStackMapOther],
+ stack_map_bits,
+ "stack map");
+ }
+ }
+ os.Stream() << "\n" << std::flush;
+ }
+
+ private:
+ void Dump(VariableIndentationOutputStream& os,
+ const char* name,
+ int64_t size,
+ int64_t total,
+ const char* sum_of = "total") {
+ const double percent = (static_cast<double>(size) / static_cast<double>(total)) * 100;
+ os.Stream() << StringPrintf("%s = %8" PRId64 " (%2.0f%% of %s)\n",
+ name,
+ size / kBitsPerByte,
+ percent,
+ sum_of);
+ }
+ };
+
private:
void AddAllOffsets() {
// We don't know the length of the code for each method, but we need to know where to stop
@@ -1046,7 +1162,9 @@
vios->Stream() << "OatQuickMethodHeader ";
uint32_t method_header_offset = oat_method.GetOatQuickMethodHeaderOffset();
const OatQuickMethodHeader* method_header = oat_method.GetOatQuickMethodHeader();
-
+ stats_.AddBitsIfUnique(Stats::kByteKindQuickMethodHeader,
+ sizeof(*method_header) * kBitsPerByte,
+ method_header);
if (options_.absolute_addresses_) {
vios->Stream() << StringPrintf("%p ", method_header);
}
@@ -1118,6 +1236,7 @@
const void* code = oat_method.GetQuickCode();
uint32_t aligned_code_begin = AlignCodeOffset(code_offset);
uint64_t aligned_code_end = aligned_code_begin + code_size;
+ stats_.AddBitsIfUnique(Stats::kByteKindCode, code_size * kBitsPerByte, code);
if (options_.absolute_addresses_) {
vios->Stream() << StringPrintf("%p ", code);
@@ -1223,7 +1342,8 @@
code_info.Dump(vios,
oat_method.GetCodeOffset(),
code_item.registers_size_,
- options_.dump_code_info_stack_maps_);
+ options_.dump_code_info_stack_maps_,
+ instruction_set_);
}
void DumpVregLocations(std::ostream& os, const OatFile::OatMethod& oat_method,
@@ -1329,21 +1449,22 @@
// For identical native PCs, the order from the CodeInfo is preserved.
class StackMapsHelper {
public:
- explicit StackMapsHelper(const uint8_t* raw_code_info)
+ explicit StackMapsHelper(const uint8_t* raw_code_info, InstructionSet instruction_set)
: code_info_(raw_code_info),
encoding_(code_info_.ExtractEncoding()),
number_of_stack_maps_(code_info_.GetNumberOfStackMaps(encoding_)),
indexes_(),
- offset_(static_cast<size_t>(-1)),
- stack_map_index_(0u) {
+ offset_(static_cast<uint32_t>(-1)),
+ stack_map_index_(0u),
+ instruction_set_(instruction_set) {
if (number_of_stack_maps_ != 0u) {
// Check if native PCs are ordered.
bool ordered = true;
StackMap last = code_info_.GetStackMapAt(0u, encoding_);
for (size_t i = 1; i != number_of_stack_maps_; ++i) {
StackMap current = code_info_.GetStackMapAt(i, encoding_);
- if (last.GetNativePcOffset(encoding_.stack_map_encoding) >
- current.GetNativePcOffset(encoding_.stack_map_encoding)) {
+ if (last.GetNativePcOffset(encoding_.stack_map_encoding, instruction_set) >
+ current.GetNativePcOffset(encoding_.stack_map_encoding, instruction_set)) {
ordered = false;
break;
}
@@ -1359,14 +1480,17 @@
indexes_.end(),
[this](size_t lhs, size_t rhs) {
StackMap left = code_info_.GetStackMapAt(lhs, encoding_);
- uint32_t left_pc = left.GetNativePcOffset(encoding_.stack_map_encoding);
+ uint32_t left_pc = left.GetNativePcOffset(encoding_.stack_map_encoding,
+ instruction_set_);
StackMap right = code_info_.GetStackMapAt(rhs, encoding_);
- uint32_t right_pc = right.GetNativePcOffset(encoding_.stack_map_encoding);
+ uint32_t right_pc = right.GetNativePcOffset(encoding_.stack_map_encoding,
+ instruction_set_);
// If the PCs are the same, compare indexes to preserve the original order.
return (left_pc < right_pc) || (left_pc == right_pc && lhs < rhs);
});
}
- offset_ = GetStackMapAt(0).GetNativePcOffset(encoding_.stack_map_encoding);
+ offset_ = GetStackMapAt(0).GetNativePcOffset(encoding_.stack_map_encoding,
+ instruction_set_);
}
}
@@ -1378,7 +1502,7 @@
return encoding_;
}
- size_t GetOffset() const {
+ uint32_t GetOffset() const {
return offset_;
}
@@ -1389,8 +1513,9 @@
void Next() {
++stack_map_index_;
offset_ = (stack_map_index_ == number_of_stack_maps_)
- ? static_cast<size_t>(-1)
- : GetStackMapAt(stack_map_index_).GetNativePcOffset(encoding_.stack_map_encoding);
+ ? static_cast<uint32_t>(-1)
+ : GetStackMapAt(stack_map_index_).GetNativePcOffset(encoding_.stack_map_encoding,
+ instruction_set_);
}
private:
@@ -1406,8 +1531,9 @@
const CodeInfoEncoding encoding_;
const size_t number_of_stack_maps_;
dchecked_vector<size_t> indexes_; // Used if stack map native PCs are not ordered.
- size_t offset_;
+ uint32_t offset_;
size_t stack_map_index_;
+ const InstructionSet instruction_set_;
};
void DumpCode(VariableIndentationOutputStream* vios,
@@ -1423,7 +1549,61 @@
return;
} else if (!bad_input && IsMethodGeneratedByOptimizingCompiler(oat_method, code_item)) {
// The optimizing compiler outputs its CodeInfo data in the vmap table.
- StackMapsHelper helper(oat_method.GetVmapTable());
+ StackMapsHelper helper(oat_method.GetVmapTable(), instruction_set_);
+ {
+ CodeInfoEncoding encoding(helper.GetEncoding());
+ StackMapEncoding stack_map_encoding(encoding.stack_map_encoding);
+ // helper.GetCodeInfo().GetStackMapAt(0, encoding).;
+ const size_t num_stack_maps = encoding.number_of_stack_maps;
+ std::vector<uint8_t> size_vector;
+ encoding.Compress(&size_vector);
+ if (stats_.AddBitsIfUnique(Stats::kByteKindCodeInfoEncoding,
+ size_vector.size() * kBitsPerByte,
+ oat_method.GetVmapTable())) {
+ stats_.AddBits(
+ Stats::kByteKindStackMapNativePc,
+ stack_map_encoding.GetNativePcEncoding().BitSize() * num_stack_maps);
+ stats_.AddBits(
+ Stats::kByteKindStackMapDexPc,
+ stack_map_encoding.GetDexPcEncoding().BitSize() * num_stack_maps);
+ stats_.AddBits(
+ Stats::kByteKindStackMapDexRegisterMap,
+ stack_map_encoding.GetDexRegisterMapEncoding().BitSize() * num_stack_maps);
+ stats_.AddBits(
+ Stats::kByteKindStackMapInlineInfo,
+ stack_map_encoding.GetInlineInfoEncoding().BitSize() * num_stack_maps);
+ stats_.AddBits(
+ Stats::kByteKindStackMapRegisterMask,
+ stack_map_encoding.GetRegisterMaskEncoding().BitSize() * num_stack_maps);
+ const size_t stack_mask_bits = encoding.stack_map_size_in_bytes * kBitsPerByte -
+ stack_map_encoding.GetStackMaskBitOffset();
+ stats_.AddBits(
+ Stats::kByteKindStackMapMask,
+ stack_mask_bits * num_stack_maps);
+ const size_t stack_map_bits =
+ stack_map_encoding.GetStackMaskBitOffset() + stack_mask_bits;
+ stats_.AddBits(
+ Stats::kByteKindStackMapOther,
+ (encoding.stack_map_size_in_bytes * kBitsPerByte - stack_map_bits) * num_stack_maps);
+ const size_t stack_map_bytes = helper.GetCodeInfo().GetStackMapsSize(encoding);
+ const size_t location_catalog_bytes =
+ helper.GetCodeInfo().GetDexRegisterLocationCatalogSize(encoding);
+ stats_.AddBits(Stats::kByteKindCodeInfoLocationCatalog,
+ kBitsPerByte * location_catalog_bytes);
+ const size_t dex_register_bytes =
+ helper.GetCodeInfo().GetDexRegisterMapsSize(encoding, code_item->registers_size_);
+ stats_.AddBits(
+ Stats::kByteKindCodeInfoDexRegisterMap,
+ kBitsPerByte * dex_register_bytes);
+ const size_t inline_info_bytes =
+ encoding.non_header_size -
+ stack_map_bytes -
+ location_catalog_bytes -
+ dex_register_bytes;
+ stats_.AddBits(Stats::kByteKindCodeInfoInlineInfo,
+ inline_info_bytes * kBitsPerByte);
+ }
+ }
const uint8_t* quick_native_pc = reinterpret_cast<const uint8_t*>(quick_code);
size_t offset = 0;
while (offset < code_size) {
@@ -1436,7 +1616,8 @@
helper.GetCodeInfo(),
helper.GetEncoding(),
oat_method.GetCodeOffset(),
- code_item->registers_size_);
+ code_item->registers_size_,
+ instruction_set_);
do {
helper.Next();
// There may be multiple stack maps at a given PC. We display only the first one.
@@ -1460,6 +1641,7 @@
const InstructionSet instruction_set_;
std::set<uintptr_t> offsets_;
Disassembler* disassembler_;
+ Stats stats_;
};
class ImageDumper {
@@ -1776,7 +1958,7 @@
os << StringPrintf("%d (0x%x)\n", field->GetShort(obj), field->GetShort(obj));
break;
case Primitive::kPrimBoolean:
- os << StringPrintf("%s (0x%x)\n", field->GetBoolean(obj)? "true" : "false",
+ os << StringPrintf("%s (0x%x)\n", field->GetBoolean(obj) ? "true" : "false",
field->GetBoolean(obj));
break;
case Primitive::kPrimByte:
@@ -2132,7 +2314,6 @@
size_t managed_code_bytes;
size_t managed_code_bytes_ignoring_deduplication;
- size_t managed_to_native_code_bytes;
size_t native_to_managed_code_bytes;
size_t class_initializer_code_bytes;
size_t large_initializer_code_bytes;
@@ -2161,7 +2342,6 @@
alignment_bytes(0),
managed_code_bytes(0),
managed_code_bytes_ignoring_deduplication(0),
- managed_to_native_code_bytes(0),
native_to_managed_code_bytes(0),
class_initializer_code_bytes(0),
large_initializer_code_bytes(0),
@@ -2359,7 +2539,6 @@
os << StringPrintf("oat_file_bytes = %8zd\n"
"managed_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
- "managed_to_native_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
"native_to_managed_code_bytes = %8zd (%2.0f%% of oat file bytes)\n\n"
"class_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
"large_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
@@ -2367,8 +2546,6 @@
oat_file_bytes,
managed_code_bytes,
PercentOfOatBytes(managed_code_bytes),
- managed_to_native_code_bytes,
- PercentOfOatBytes(managed_to_native_code_bytes),
native_to_managed_code_bytes,
PercentOfOatBytes(native_to_managed_code_bytes),
class_initializer_code_bytes,
diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc
index e77d03b..ba57d18 100644
--- a/oatdump/oatdump_test.cc
+++ b/oatdump/oatdump_test.cc
@@ -102,6 +102,7 @@
// Code and dex code do not show up if list only.
expected_prefixes.push_back("DEX CODE:");
expected_prefixes.push_back("CODE:");
+ expected_prefixes.push_back("CodeInfoEncoding");
}
if (mode == kModeArt) {
exec_argv.push_back("--image=" + core_art_location_);
diff --git a/profman/profile_assistant.h b/profman/profile_assistant.h
index d3c75b8..be703ab 100644
--- a/profman/profile_assistant.h
+++ b/profman/profile_assistant.h
@@ -21,7 +21,7 @@
#include <vector>
#include "base/scoped_flock.h"
-#include "jit/offline_profiling_info.h"
+#include "jit/profile_compilation_info.h"
namespace art {
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index 776c31a..2f40fef 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -19,7 +19,7 @@
#include "base/unix_file/fd_file.h"
#include "common_runtime_test.h"
#include "profile_assistant.h"
-#include "jit/offline_profiling_info.h"
+#include "jit/profile_compilation_info.h"
#include "utils.h"
namespace art {
diff --git a/profman/profman.cc b/profman/profman.cc
index e538407..ffebb6a 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -34,7 +34,7 @@
#include "base/time_utils.h"
#include "base/unix_file/fd_file.h"
#include "dex_file.h"
-#include "jit/offline_profiling_info.h"
+#include "jit/profile_compilation_info.h"
#include "runtime.h"
#include "utils.h"
#include "zip_archive.h"
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 86019bf..81f174e 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -112,7 +112,7 @@
"jit/debugger_interface.cc",
"jit/jit.cc",
"jit/jit_code_cache.cc",
- "jit/offline_profiling_info.cc",
+ "jit/profile_compilation_info.cc",
"jit/profiling_info.cc",
"jit/profile_saver.cc",
"jni_internal.cc",
@@ -184,6 +184,7 @@
"reference_table.cc",
"reflection.cc",
"runtime.cc",
+ "runtime_callbacks.cc",
"runtime_options.cc",
"signal_catcher.cc",
"stack.cc",
@@ -563,6 +564,7 @@
"parsed_options_test.cc",
"prebuilt_tools_test.cc",
"reference_table_test.cc",
+ "runtime_callbacks_test.cc",
"thread_pool_test.cc",
"transaction_test.cc",
"type_lookup_table_test.cc",
diff --git a/runtime/arch/arm/quick_method_frame_info_arm.h b/runtime/arch/arm/quick_method_frame_info_arm.h
index 4b23c77..35f1948 100644
--- a/runtime/arch/arm/quick_method_frame_info_arm.h
+++ b/runtime/arch/arm/quick_method_frame_info_arm.h
@@ -62,7 +62,7 @@
constexpr uint32_t ArmCalleeSaveFpSpills(Runtime::CalleeSaveType type) {
return kArmCalleeSaveFpAlwaysSpills | kArmCalleeSaveFpRefSpills |
- (type == Runtime::kSaveRefsAndArgs ? kArmCalleeSaveFpArgSpills: 0) |
+ (type == Runtime::kSaveRefsAndArgs ? kArmCalleeSaveFpArgSpills : 0) |
(type == Runtime::kSaveAllCalleeSaves ? kArmCalleeSaveFpAllSpills : 0) |
(type == Runtime::kSaveEverything ? kArmCalleeSaveFpEverythingSpills : 0);
}
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc
index c598743..01bd177 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64.cc
@@ -33,7 +33,16 @@
const std::string& variant, std::string* error_msg) {
// Look for variants that need a fix for a53 erratum 835769.
static const char* arm64_variants_with_a53_835769_bug[] = {
- "default", "generic", "cortex-a53" // Pessimistically assume all generic ARM64s are A53s.
+ // Pessimistically assume all generic CPUs are cortex-a53.
+ "default",
+ "generic",
+ "cortex-a53",
+ "cortex-a53.a57",
+ "cortex-a53.a72",
+ // Pessimistically assume all "big" cortex CPUs are paired with a cortex-a53.
+ "cortex-a57",
+ "cortex-a72",
+ "cortex-a73",
};
bool needs_a53_835769_fix = FindVariantInArray(arm64_variants_with_a53_835769_bug,
arraysize(arm64_variants_with_a53_835769_bug),
@@ -42,7 +51,10 @@
if (!needs_a53_835769_fix) {
// Check to see if this is an expected variant.
static const char* arm64_known_variants[] = {
- "denver64", "kryo", "exynos-m1"
+ "cortex-a35",
+ "exynos-m1",
+ "denver64",
+ "kryo"
};
if (!FindVariantInArray(arm64_known_variants, arraysize(arm64_known_variants), variant)) {
std::ostringstream os;
diff --git a/runtime/arch/arm64/instruction_set_features_arm64_test.cc b/runtime/arch/arm64/instruction_set_features_arm64_test.cc
index cefa499..91cb58f 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64_test.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64_test.cc
@@ -30,6 +30,40 @@
EXPECT_TRUE(arm64_features->Equals(arm64_features.get()));
EXPECT_STREQ("a53", arm64_features->GetFeatureString().c_str());
EXPECT_EQ(arm64_features->AsBitmap(), 1U);
+
+ std::unique_ptr<const InstructionSetFeatures> cortex_a57_features(
+ InstructionSetFeatures::FromVariant(kArm64, "cortex-a57", &error_msg));
+ ASSERT_TRUE(cortex_a57_features.get() != nullptr) << error_msg;
+ EXPECT_EQ(cortex_a57_features->GetInstructionSet(), kArm64);
+ EXPECT_TRUE(cortex_a57_features->Equals(cortex_a57_features.get()));
+ EXPECT_STREQ("a53", cortex_a57_features->GetFeatureString().c_str());
+ EXPECT_EQ(cortex_a57_features->AsBitmap(), 1U);
+
+ std::unique_ptr<const InstructionSetFeatures> cortex_a73_features(
+ InstructionSetFeatures::FromVariant(kArm64, "cortex-a73", &error_msg));
+ ASSERT_TRUE(cortex_a73_features.get() != nullptr) << error_msg;
+ EXPECT_EQ(cortex_a73_features->GetInstructionSet(), kArm64);
+ EXPECT_TRUE(cortex_a73_features->Equals(cortex_a73_features.get()));
+ EXPECT_STREQ("a53", cortex_a73_features->GetFeatureString().c_str());
+ EXPECT_EQ(cortex_a73_features->AsBitmap(), 1U);
+
+ std::unique_ptr<const InstructionSetFeatures> cortex_a35_features(
+ InstructionSetFeatures::FromVariant(kArm64, "cortex-a35", &error_msg));
+ ASSERT_TRUE(cortex_a35_features.get() != nullptr) << error_msg;
+ EXPECT_EQ(cortex_a35_features->GetInstructionSet(), kArm64);
+ EXPECT_TRUE(cortex_a35_features->Equals(cortex_a35_features.get()));
+ EXPECT_STREQ("-a53", cortex_a35_features->GetFeatureString().c_str());
+ EXPECT_EQ(cortex_a35_features->AsBitmap(), 0U);
+
+ std::unique_ptr<const InstructionSetFeatures> kryo_features(
+ InstructionSetFeatures::FromVariant(kArm64, "kryo", &error_msg));
+ ASSERT_TRUE(kryo_features.get() != nullptr) << error_msg;
+ EXPECT_EQ(kryo_features->GetInstructionSet(), kArm64);
+ EXPECT_TRUE(kryo_features->Equals(kryo_features.get()));
+ EXPECT_TRUE(kryo_features->Equals(cortex_a35_features.get()));
+ EXPECT_FALSE(kryo_features->Equals(cortex_a57_features.get()));
+ EXPECT_STREQ("-a53", kryo_features->GetFeatureString().c_str());
+ EXPECT_EQ(kryo_features->AsBitmap(), 0U);
}
} // namespace art
diff --git a/runtime/arch/arm64/quick_method_frame_info_arm64.h b/runtime/arch/arm64/quick_method_frame_info_arm64.h
index 36f283b..32d9d08 100644
--- a/runtime/arch/arm64/quick_method_frame_info_arm64.h
+++ b/runtime/arch/arm64/quick_method_frame_info_arm64.h
@@ -85,7 +85,7 @@
constexpr uint32_t Arm64CalleeSaveFpSpills(Runtime::CalleeSaveType type) {
return kArm64CalleeSaveFpAlwaysSpills | kArm64CalleeSaveFpRefSpills |
- (type == Runtime::kSaveRefsAndArgs ? kArm64CalleeSaveFpArgSpills: 0) |
+ (type == Runtime::kSaveRefsAndArgs ? kArm64CalleeSaveFpArgSpills : 0) |
(type == Runtime::kSaveAllCalleeSaves ? kArm64CalleeSaveFpAllSpills : 0) |
(type == Runtime::kSaveEverything ? kArm64CalleeSaveFpEverythingSpills : 0);
}
diff --git a/runtime/arch/code_offset.h b/runtime/arch/code_offset.h
new file mode 100644
index 0000000..ab04b1e
--- /dev/null
+++ b/runtime/arch/code_offset.h
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_ARCH_CODE_OFFSET_H_
+#define ART_RUNTIME_ARCH_CODE_OFFSET_H_
+
+#include <iosfwd>
+
+#include "base/bit_utils.h"
+#include "base/logging.h"
+#include "instruction_set.h"
+
+namespace art {
+
+// CodeOffset is a holder for compressed code offsets. Since some architectures have alignment
+// requirements it is possible to compress code offsets to reduce stack map sizes.
+class CodeOffset {
+ public:
+ ALWAYS_INLINE static CodeOffset FromOffset(uint32_t offset, InstructionSet isa = kRuntimeISA) {
+ return CodeOffset(offset / GetInstructionSetInstructionAlignment(isa));
+ }
+
+ ALWAYS_INLINE static CodeOffset FromCompressedOffset(uint32_t offset) {
+ return CodeOffset(offset);
+ }
+
+ ALWAYS_INLINE uint32_t Uint32Value(InstructionSet isa = kRuntimeISA) const {
+ uint32_t decoded = value_ * GetInstructionSetInstructionAlignment(isa);
+ DCHECK_GE(decoded, value_) << "Integer overflow";
+ return decoded;
+ }
+
+ // Return compressed internal value.
+ ALWAYS_INLINE uint32_t CompressedValue() const {
+ return value_;
+ }
+
+ ALWAYS_INLINE CodeOffset() = default;
+ ALWAYS_INLINE CodeOffset(const CodeOffset&) = default;
+ ALWAYS_INLINE CodeOffset& operator=(const CodeOffset&) = default;
+ ALWAYS_INLINE CodeOffset& operator=(CodeOffset&&) = default;
+
+ private:
+ ALWAYS_INLINE explicit CodeOffset(uint32_t value) : value_(value) {}
+
+ uint32_t value_ = 0u;
+};
+
+inline bool operator==(const CodeOffset& a, const CodeOffset& b) {
+ return a.CompressedValue() == b.CompressedValue();
+}
+
+inline bool operator!=(const CodeOffset& a, const CodeOffset& b) {
+ return !(a == b);
+}
+
+inline bool operator<(const CodeOffset& a, const CodeOffset& b) {
+ return a.CompressedValue() < b.CompressedValue();
+}
+
+inline bool operator<=(const CodeOffset& a, const CodeOffset& b) {
+ return a.CompressedValue() <= b.CompressedValue();
+}
+
+inline bool operator>(const CodeOffset& a, const CodeOffset& b) {
+ return a.CompressedValue() > b.CompressedValue();
+}
+
+inline bool operator>=(const CodeOffset& a, const CodeOffset& b) {
+ return a.CompressedValue() >= b.CompressedValue();
+}
+
+inline std::ostream& operator<<(std::ostream& os, const CodeOffset& offset) {
+ return os << offset.Uint32Value();
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_ARCH_CODE_OFFSET_H_
diff --git a/runtime/arch/instruction_set.h b/runtime/arch/instruction_set.h
index 4a8bea4..99aea62 100644
--- a/runtime/arch/instruction_set.h
+++ b/runtime/arch/instruction_set.h
@@ -75,6 +75,14 @@
// X86 instruction alignment. This is the recommended alignment for maximum performance.
static constexpr size_t kX86Alignment = 16;
+// Different than code alignment since code alignment is only first instruction of method.
+static constexpr size_t kThumb2InstructionAlignment = 2;
+static constexpr size_t kArm64InstructionAlignment = 4;
+static constexpr size_t kX86InstructionAlignment = 1;
+static constexpr size_t kX86_64InstructionAlignment = 1;
+static constexpr size_t kMipsInstructionAlignment = 2;
+static constexpr size_t kMips64InstructionAlignment = 2;
+
const char* GetInstructionSetString(InstructionSet isa);
// Note: Returns kNone when the string cannot be parsed to a known value.
@@ -106,6 +114,17 @@
}
}
+ALWAYS_INLINE static inline constexpr size_t GetInstructionSetInstructionAlignment(
+ InstructionSet isa) {
+ return (isa == kThumb2 || isa == kArm) ? kThumb2InstructionAlignment :
+ (isa == kArm64) ? kArm64InstructionAlignment :
+ (isa == kX86) ? kX86InstructionAlignment :
+ (isa == kX86_64) ? kX86_64InstructionAlignment :
+ (isa == kMips) ? kMipsInstructionAlignment :
+ (isa == kMips64) ? kMips64InstructionAlignment :
+ 0; // Invalid case, but constexpr doesn't support asserts.
+}
+
static inline bool IsValidInstructionSet(InstructionSet isa) {
switch (isa) {
case kArm:
diff --git a/runtime/arch/instruction_set_test.cc b/runtime/arch/instruction_set_test.cc
index 5aae93a..b251b57 100644
--- a/runtime/arch/instruction_set_test.cc
+++ b/runtime/arch/instruction_set_test.cc
@@ -44,6 +44,15 @@
EXPECT_STREQ("none", GetInstructionSetString(kNone));
}
+TEST(InstructionSetTest, GetInstructionSetInstructionAlignment) {
+ EXPECT_EQ(GetInstructionSetInstructionAlignment(kThumb2), kThumb2InstructionAlignment);
+ EXPECT_EQ(GetInstructionSetInstructionAlignment(kArm64), kArm64InstructionAlignment);
+ EXPECT_EQ(GetInstructionSetInstructionAlignment(kX86), kX86InstructionAlignment);
+ EXPECT_EQ(GetInstructionSetInstructionAlignment(kX86_64), kX86_64InstructionAlignment);
+ EXPECT_EQ(GetInstructionSetInstructionAlignment(kMips), kMipsInstructionAlignment);
+ EXPECT_EQ(GetInstructionSetInstructionAlignment(kMips64), kMips64InstructionAlignment);
+}
+
TEST(InstructionSetTest, TestRoundTrip) {
EXPECT_EQ(kRuntimeISA, GetInstructionSetFromString(GetInstructionSetString(kRuntimeISA)));
}
diff --git a/runtime/arch/mips64/quick_method_frame_info_mips64.h b/runtime/arch/mips64/quick_method_frame_info_mips64.h
index 397776e..d774473 100644
--- a/runtime/arch/mips64/quick_method_frame_info_mips64.h
+++ b/runtime/arch/mips64/quick_method_frame_info_mips64.h
@@ -78,7 +78,7 @@
constexpr uint32_t Mips64CalleeSaveFpSpills(Runtime::CalleeSaveType type) {
return kMips64CalleeSaveFpRefSpills |
- (type == Runtime::kSaveRefsAndArgs ? kMips64CalleeSaveFpArgSpills: 0) |
+ (type == Runtime::kSaveRefsAndArgs ? kMips64CalleeSaveFpArgSpills : 0) |
(type == Runtime::kSaveAllCalleeSaves ? kMips64CalleeSaveFpAllSpills : 0) |
(type == Runtime::kSaveEverything ? kMips64CalleeSaveFpEverythingSpills : 0);
}
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 49cffed..b8ed530 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -65,7 +65,7 @@
#include "interpreter/interpreter.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
-#include "jit/offline_profiling_info.h"
+#include "jit/profile_compilation_info.h"
#include "jni_internal.h"
#include "leb128.h"
#include "linear_alloc.h"
@@ -96,6 +96,7 @@
#include "object_lock.h"
#include "os.h"
#include "runtime.h"
+#include "runtime_callbacks.h"
#include "ScopedLocalRef.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-inl.h"
@@ -1397,7 +1398,11 @@
class_loader_(class_loader) {}
bool operator()(ObjPtr<mirror::Class> klass) const REQUIRES_SHARED(Locks::mutator_lock_) {
- klass->SetClassLoader(class_loader_);
+ // Do not update class loader for boot image classes where the app image
+ // class loader is only the initiating loader but not the defining loader.
+ if (klass->GetClassLoader() != nullptr) {
+ klass->SetClassLoader(class_loader_);
+ }
return true;
}
@@ -2457,10 +2462,8 @@
return EnsureResolved(self, descriptor, klass);
}
// Class is not yet loaded.
- if (descriptor[0] == '[') {
- return CreateArrayClass(self, descriptor, hash, class_loader);
- } else if (class_loader.Get() == nullptr) {
- // The boot class loader, search the boot class path.
+ if (descriptor[0] != '[' && class_loader.Get() == nullptr) {
+ // Non-array class and the boot class loader, search the boot class path.
ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
if (pair.second != nullptr) {
return DefineClass(self,
@@ -2473,14 +2476,21 @@
// The boot class loader is searched ahead of the application class loader, failures are
// expected and will be wrapped in a ClassNotFoundException. Use the pre-allocated error to
// trigger the chaining with a proper stack trace.
- ObjPtr<mirror::Throwable> pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
+ ObjPtr<mirror::Throwable> pre_allocated =
+ Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
self->SetException(pre_allocated);
return nullptr;
}
+ }
+ ObjPtr<mirror::Class> result_ptr;
+ bool descriptor_equals;
+ if (descriptor[0] == '[') {
+ result_ptr = CreateArrayClass(self, descriptor, hash, class_loader);
+ DCHECK_EQ(result_ptr == nullptr, self->IsExceptionPending());
+ DCHECK(result_ptr == nullptr || result_ptr->DescriptorEquals(descriptor));
+ descriptor_equals = true;
} else {
ScopedObjectAccessUnchecked soa(self);
- ObjPtr<mirror::Class> result_ptr;
- bool descriptor_equals;
bool known_hierarchy =
FindClassInBaseDexClassLoader(soa, self, descriptor, hash, class_loader, &result_ptr);
if (result_ptr != nullptr) {
@@ -2524,16 +2534,7 @@
WellKnownClasses::java_lang_ClassLoader_loadClass,
class_name_object.get()));
}
- if (self->IsExceptionPending()) {
- // If the ClassLoader threw, pass that exception up.
- // However, to comply with the RI behavior, first check if another thread succeeded.
- result_ptr = LookupClass(self, descriptor, hash, class_loader.Get());
- if (result_ptr != nullptr && !result_ptr->IsErroneous()) {
- self->ClearException();
- return EnsureResolved(self, descriptor, result_ptr);
- }
- return nullptr;
- } else if (result.get() == nullptr) {
+ if (result.get() == nullptr && !self->IsExceptionPending()) {
// broken loader - throw NPE to be compatible with Dalvik
ThrowNullPointerException(StringPrintf("ClassLoader.loadClass returned null for %s",
class_name_string.c_str()).c_str());
@@ -2541,50 +2542,60 @@
}
result_ptr = soa.Decode<mirror::Class>(result.get());
// Check the name of the returned class.
- descriptor_equals = result_ptr->DescriptorEquals(descriptor);
+ descriptor_equals = (result_ptr != nullptr) && result_ptr->DescriptorEquals(descriptor);
}
-
- // Try to insert the class to the class table, checking for mismatch.
- ObjPtr<mirror::Class> old;
- {
- WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
- ClassTable* const class_table = InsertClassTableForClassLoader(class_loader.Get());
- old = class_table->Lookup(descriptor, hash);
- if (old == nullptr) {
- old = result_ptr; // For the comparison below, after releasing the lock.
- if (descriptor_equals) {
- class_table->InsertWithHash(result_ptr.Ptr(), hash);
- Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get());
- } // else throw below, after releasing the lock.
- }
- }
- if (UNLIKELY(old != result_ptr)) {
- // Return `old` (even if `!descriptor_equals`) to mimic the RI behavior for parallel
- // capable class loaders. (All class loaders are considered parallel capable on Android.)
- mirror::Class* loader_class = class_loader->GetClass();
- const char* loader_class_name =
- loader_class->GetDexFile().StringByTypeIdx(loader_class->GetDexTypeIndex());
- LOG(WARNING) << "Initiating class loader of type " << DescriptorToDot(loader_class_name)
- << " is not well-behaved; it returned a different Class for racing loadClass(\""
- << DescriptorToDot(descriptor) << "\").";
- return EnsureResolved(self, descriptor, old);
- }
- if (UNLIKELY(!descriptor_equals)) {
- std::string result_storage;
- const char* result_name = result_ptr->GetDescriptor(&result_storage);
- std::string loader_storage;
- const char* loader_class_name = class_loader->GetClass()->GetDescriptor(&loader_storage);
- ThrowNoClassDefFoundError(
- "Initiating class loader of type %s returned class %s instead of %s.",
- DescriptorToDot(loader_class_name).c_str(),
- DescriptorToDot(result_name).c_str(),
- DescriptorToDot(descriptor).c_str());
- return nullptr;
- }
- // success, return mirror::Class*
- return result_ptr.Ptr();
}
- UNREACHABLE();
+
+ if (self->IsExceptionPending()) {
+ // If the ClassLoader threw or array class allocation failed, pass that exception up.
+ // However, to comply with the RI behavior, first check if another thread succeeded.
+ result_ptr = LookupClass(self, descriptor, hash, class_loader.Get());
+ if (result_ptr != nullptr && !result_ptr->IsErroneous()) {
+ self->ClearException();
+ return EnsureResolved(self, descriptor, result_ptr);
+ }
+ return nullptr;
+ }
+
+ // Try to insert the class to the class table, checking for mismatch.
+ ObjPtr<mirror::Class> old;
+ {
+ WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
+ ClassTable* const class_table = InsertClassTableForClassLoader(class_loader.Get());
+ old = class_table->Lookup(descriptor, hash);
+ if (old == nullptr) {
+ old = result_ptr; // For the comparison below, after releasing the lock.
+ if (descriptor_equals) {
+ class_table->InsertWithHash(result_ptr.Ptr(), hash);
+ Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get());
+ } // else throw below, after releasing the lock.
+ }
+ }
+ if (UNLIKELY(old != result_ptr)) {
+ // Return `old` (even if `!descriptor_equals`) to mimic the RI behavior for parallel
+ // capable class loaders. (All class loaders are considered parallel capable on Android.)
+ mirror::Class* loader_class = class_loader->GetClass();
+ const char* loader_class_name =
+ loader_class->GetDexFile().StringByTypeIdx(loader_class->GetDexTypeIndex());
+ LOG(WARNING) << "Initiating class loader of type " << DescriptorToDot(loader_class_name)
+ << " is not well-behaved; it returned a different Class for racing loadClass(\""
+ << DescriptorToDot(descriptor) << "\").";
+ return EnsureResolved(self, descriptor, old);
+ }
+ if (UNLIKELY(!descriptor_equals)) {
+ std::string result_storage;
+ const char* result_name = result_ptr->GetDescriptor(&result_storage);
+ std::string loader_storage;
+ const char* loader_class_name = class_loader->GetClass()->GetDescriptor(&loader_storage);
+ ThrowNoClassDefFoundError(
+ "Initiating class loader of type %s returned class %s instead of %s.",
+ DescriptorToDot(loader_class_name).c_str(),
+ DescriptorToDot(result_name).c_str(),
+ DescriptorToDot(descriptor).c_str());
+ return nullptr;
+ }
+ // success, return mirror::Class*
+ return result_ptr.Ptr();
}
mirror::Class* ClassLinker::DefineClass(Thread* self,
@@ -2625,13 +2636,26 @@
self->AssertPendingOOMException();
return nullptr;
}
- ObjPtr<mirror::DexCache> dex_cache = RegisterDexFile(dex_file, class_loader.Get());
+ // Get the real dex file. This will return the input if there aren't any callbacks or they do
+ // nothing.
+ DexFile const* new_dex_file = nullptr;
+ DexFile::ClassDef const* new_class_def = nullptr;
+ // TODO We should ideally figure out some way to move this after we get a lock on the klass so it
+ // will only be called once.
+ Runtime::Current()->GetRuntimeCallbacks()->ClassPreDefine(descriptor,
+ klass,
+ class_loader,
+ dex_file,
+ dex_class_def,
+ &new_dex_file,
+ &new_class_def);
+ ObjPtr<mirror::DexCache> dex_cache = RegisterDexFile(*new_dex_file, class_loader.Get());
if (dex_cache == nullptr) {
self->AssertPendingOOMException();
return nullptr;
}
klass->SetDexCache(dex_cache);
- SetupClass(dex_file, dex_class_def, klass, class_loader.Get());
+ SetupClass(*new_dex_file, *new_class_def, klass, class_loader.Get());
// Mark the string class by setting its access flag.
if (UNLIKELY(!init_done_)) {
@@ -2657,7 +2681,7 @@
// end up allocating unfree-able linear alloc resources and then lose the race condition. The
// other reason is that the field roots are only visited from the class table. So we need to be
// inserted before we allocate / fill in these fields.
- LoadClass(self, dex_file, dex_class_def, klass);
+ LoadClass(self, *new_dex_file, *new_class_def, klass);
if (self->IsExceptionPending()) {
VLOG(class_linker) << self->GetException()->Dump();
// An exception occured during load, set status to erroneous while holding klass' lock in case
@@ -2670,7 +2694,7 @@
// Finish loading (if necessary) by finding parents
CHECK(!klass->IsLoaded());
- if (!LoadSuperAndInterfaces(klass, dex_file)) {
+ if (!LoadSuperAndInterfaces(klass, *new_dex_file)) {
// Loading failed.
if (!klass->IsErroneous()) {
mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
@@ -2678,6 +2702,11 @@
return nullptr;
}
CHECK(klass->IsLoaded());
+
+ // At this point the class is loaded. Publish a ClassLoad even.
+ // Note: this may be a temporary class. It is a listener's responsibility to handle this.
+ Runtime::Current()->GetRuntimeCallbacks()->ClassLoad(klass);
+
// Link the class (if necessary)
CHECK(!klass->IsResolved());
// TODO: Use fast jobjects?
@@ -2718,7 +2747,7 @@
* The class has been prepared and resolved but possibly not yet verified
* at this point.
*/
- Dbg::PostClassPrepare(h_new_class.Get());
+ Runtime::Current()->GetRuntimeCallbacks()->ClassPrepare(klass, h_new_class);
// Notify native debugger of the new class and its layout.
jit::Jit::NewTypeLoadedIfUsingJit(h_new_class.Get());
@@ -3488,7 +3517,8 @@
// class to the hash table --- necessary because of possible races with
// other threads.)
if (class_loader.Get() != component_type->GetClassLoader()) {
- ObjPtr<mirror::Class> new_class = LookupClass(self, descriptor, hash, component_type->GetClassLoader());
+ ObjPtr<mirror::Class> new_class =
+ LookupClass(self, descriptor, hash, component_type->GetClassLoader());
if (new_class != nullptr) {
return new_class.Ptr();
}
@@ -7706,7 +7736,7 @@
type = LookupClass(self, descriptor, hash, class_loader.Ptr());
}
}
- if (type != nullptr || type->IsResolved()) {
+ if (type != nullptr && type->IsResolved()) {
return type.Ptr();
}
return nullptr;
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 9b98671..d3bb58d 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -34,6 +34,7 @@
#include "dex_file.h"
#include "dex_file_types.h"
#include "gc_root.h"
+#include "handle.h"
#include "jni.h"
#include "mirror/class.h"
#include "object_callbacks.h"
@@ -1194,6 +1195,38 @@
DISALLOW_COPY_AND_ASSIGN(ClassLinker);
};
+class ClassLoadCallback {
+ public:
+ virtual ~ClassLoadCallback() {}
+
+ // If set we will replace initial_class_def & initial_dex_file with the final versions. The
+ // callback author is responsible for ensuring these are allocated in such a way they can be
+ // cleaned up if another transformation occurs. Note that both must be set or null/unchanged on
+ // return.
+ // Note: the class may be temporary, in which case a following ClassPrepare event will be a
+ // different object. It is the listener's responsibility to handle this.
+ // Note: This callback is rarely useful so a default implementation has been given that does
+ // nothing.
+ virtual void ClassPreDefine(const char* descriptor ATTRIBUTE_UNUSED,
+ Handle<mirror::Class> klass ATTRIBUTE_UNUSED,
+ Handle<mirror::ClassLoader> class_loader ATTRIBUTE_UNUSED,
+ const DexFile& initial_dex_file ATTRIBUTE_UNUSED,
+ const DexFile::ClassDef& initial_class_def ATTRIBUTE_UNUSED,
+ /*out*/DexFile const** final_dex_file ATTRIBUTE_UNUSED,
+ /*out*/DexFile::ClassDef const** final_dex_cache ATTRIBUTE_UNUSED)
+ REQUIRES_SHARED(Locks::mutator_lock_) {}
+
+ // A class has been loaded.
+ // Note: the class may be temporary, in which case a following ClassPrepare event will be a
+ // different object. It is the listener's responsibility to handle this.
+ virtual void ClassLoad(Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+
+ // A class has been prepared, i.e., resolved. As the ClassLoad event might have been for a
+ // temporary class, provide both the former and the current class.
+ virtual void ClassPrepare(Handle<mirror::Class> temp_klass,
+ Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+};
+
} // namespace art
#endif // ART_RUNTIME_CLASS_LINKER_H_
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 0341c64..7b6c0dc 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -487,7 +487,7 @@
// says AccessibleObject is 9 bytes but sizeof(AccessibleObject) is 12 bytes due to padding.
// The RoundUp is to get around this case.
static constexpr size_t kPackAlignment = 4;
- size_t expected_size = RoundUp(is_static ? klass->GetClassSize(): klass->GetObjectSize(),
+ size_t expected_size = RoundUp(is_static ? klass->GetClassSize() : klass->GetObjectSize(),
kPackAlignment);
if (sizeof(T) != expected_size) {
LOG(ERROR) << "Class size mismatch:"
@@ -612,7 +612,7 @@
ClassExtOffsets() : CheckOffsets<mirror::ClassExt>(false, "Ldalvik/system/ClassExt;") {
addOffset(OFFSETOF_MEMBER(mirror::ClassExt, obsolete_dex_caches_), "obsoleteDexCaches");
addOffset(OFFSETOF_MEMBER(mirror::ClassExt, obsolete_methods_), "obsoleteMethods");
- addOffset(OFFSETOF_MEMBER(mirror::ClassExt, original_dex_cache_), "originalDexCache");
+ addOffset(OFFSETOF_MEMBER(mirror::ClassExt, original_dex_file_bytes_), "originalDexFile");
addOffset(OFFSETOF_MEMBER(mirror::ClassExt, verify_error_), "verifyError");
}
};
@@ -906,6 +906,41 @@
klass);
}
+TEST_F(ClassLinkerTest, LookupResolvedTypeArray) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("AllFields"))));
+ // Get the AllFields class for the dex cache and dex file.
+ ObjPtr<mirror::Class> all_fields_klass
+ = class_linker_->FindClass(soa.Self(), "LAllFields;", class_loader);
+ ASSERT_OBJ_PTR_NE(all_fields_klass, ObjPtr<mirror::Class>(nullptr));
+ Handle<mirror::DexCache> dex_cache = hs.NewHandle(all_fields_klass->GetDexCache());
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ // Get the index of the array class we want to test.
+ const DexFile::TypeId* array_id = dex_file.FindTypeId("[Ljava/lang/Object;");
+ ASSERT_TRUE(array_id != nullptr);
+ dex::TypeIndex array_idx = dex_file.GetIndexForTypeId(*array_id);
+ // Check that the array class wasn't resolved yet.
+ EXPECT_OBJ_PTR_EQ(
+ class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()),
+ ObjPtr<mirror::Class>(nullptr));
+ // Resolve the array class we want to test.
+ ObjPtr<mirror::Class> array_klass
+ = class_linker_->FindClass(soa.Self(), "[Ljava/lang/Object;", class_loader);
+ ASSERT_OBJ_PTR_NE(array_klass, ObjPtr<mirror::Class>(nullptr));
+ // Test that LookupResolvedType() finds the array class.
+ EXPECT_OBJ_PTR_EQ(
+ class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()),
+ array_klass);
+ // Zero out the resolved type and make sure LookupResolvedType() still finds it.
+ dex_cache->SetResolvedType(array_idx, nullptr);
+ EXPECT_TRUE(dex_cache->GetResolvedType(array_idx) == nullptr);
+ EXPECT_OBJ_PTR_EQ(
+ class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()),
+ array_klass);
+}
+
TEST_F(ClassLinkerTest, LibCore) {
ScopedObjectAccess soa(Thread::Current());
ASSERT_TRUE(java_lang_dex_file_ != nullptr);
diff --git a/runtime/class_table.cc b/runtime/class_table.cc
index 0f985c6..ff846a7 100644
--- a/runtime/class_table.cc
+++ b/runtime/class_table.cc
@@ -129,6 +129,19 @@
classes_.back().InsertWithHash(TableSlot(klass, hash), hash);
}
+void ClassTable::CopyWithoutLocks(const ClassTable& source_table) {
+ if (kIsDebugBuild) {
+ for (ClassSet& class_set : classes_) {
+ CHECK(class_set.Empty());
+ }
+ }
+ for (const ClassSet& class_set : source_table.classes_) {
+ for (const TableSlot& slot : class_set) {
+ classes_.back().Insert(slot);
+ }
+ }
+}
+
void ClassTable::InsertWithoutLocks(ObjPtr<mirror::Class> klass) {
const uint32_t hash = TableSlot::HashDescriptor(klass);
classes_.back().InsertWithHash(TableSlot(klass, hash), hash);
diff --git a/runtime/class_table.h b/runtime/class_table.h
index f27d809..c8ec28e 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -240,6 +240,7 @@
}
private:
+ void CopyWithoutLocks(const ClassTable& source_table) NO_THREAD_SAFETY_ANALYSIS;
void InsertWithoutLocks(ObjPtr<mirror::Class> klass) NO_THREAD_SAFETY_ANALYSIS;
size_t CountDefiningLoaderClasses(ObjPtr<mirror::ClassLoader> defining_loader,
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 743fcc8..fc82264 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -133,7 +133,9 @@
static bool unstarted_initialized_ = false;
-CommonRuntimeTestImpl::CommonRuntimeTestImpl() {}
+CommonRuntimeTestImpl::CommonRuntimeTestImpl()
+ : class_linker_(nullptr), java_lang_dex_file_(nullptr) {
+}
CommonRuntimeTestImpl::~CommonRuntimeTestImpl() {
// Ensure the dex files are cleaned up before the runtime.
@@ -425,7 +427,9 @@
TearDownAndroidData(android_data_, true);
dalvik_cache_.clear();
- Runtime::Current()->GetHeap()->VerifyHeap(); // Check for heap corruption after the test
+ if (runtime_ != nullptr) {
+ runtime_->GetHeap()->VerifyHeap(); // Check for heap corruption after the test
+ }
}
static std::string GetDexFileName(const std::string& jar_prefix, bool host) {
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 006476b..22a3163 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -320,6 +320,9 @@
size_t Dbg::exception_catch_event_ref_count_ = 0;
uint32_t Dbg::instrumentation_events_ = 0;
+Dbg::DbgThreadLifecycleCallback Dbg::thread_lifecycle_callback_;
+Dbg::DbgClassLoadCallback Dbg::class_load_callback_;
+
// Breakpoints.
static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_);
@@ -5135,4 +5138,20 @@
}
}
+void Dbg::DbgThreadLifecycleCallback::ThreadStart(Thread* self) {
+ Dbg::PostThreadStart(self);
+}
+
+void Dbg::DbgThreadLifecycleCallback::ThreadDeath(Thread* self) {
+ Dbg::PostThreadDeath(self);
+}
+
+void Dbg::DbgClassLoadCallback::ClassLoad(Handle<mirror::Class> klass ATTRIBUTE_UNUSED) {
+ // Ignore ClassLoad;
+}
+void Dbg::DbgClassLoadCallback::ClassPrepare(Handle<mirror::Class> temp_klass ATTRIBUTE_UNUSED,
+ Handle<mirror::Class> klass) {
+ Dbg::PostClassPrepare(klass.Get());
+}
+
} // namespace art
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 3b4a5e1..a7fd160 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -28,6 +28,8 @@
#include <vector>
#include "gc_root.h"
+#include "class_linker.h"
+#include "handle.h"
#include "jdwp/jdwp.h"
#include "jni.h"
#include "jvalue.h"
@@ -502,12 +504,6 @@
REQUIRES_SHARED(Locks::mutator_lock_);
static void PostException(mirror::Throwable* exception)
REQUIRES_SHARED(Locks::mutator_lock_);
- static void PostThreadStart(Thread* t)
- REQUIRES_SHARED(Locks::mutator_lock_);
- static void PostThreadDeath(Thread* t)
- REQUIRES_SHARED(Locks::mutator_lock_);
- static void PostClassPrepare(mirror::Class* c)
- REQUIRES_SHARED(Locks::mutator_lock_);
static void UpdateDebugger(Thread* thread, mirror::Object* this_object,
ArtMethod* method, uint32_t new_dex_pc,
@@ -707,6 +703,13 @@
return instrumentation_events_;
}
+ static ThreadLifecycleCallback* GetThreadLifecycleCallback() {
+ return &thread_lifecycle_callback_;
+ }
+ static ClassLoadCallback* GetClassLoadCallback() {
+ return &class_load_callback_;
+ }
+
private:
static void ExecuteMethodWithoutPendingException(ScopedObjectAccess& soa, DebugInvokeReq* pReq)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -725,9 +728,17 @@
REQUIRES(!Locks::thread_list_lock_) REQUIRES_SHARED(Locks::mutator_lock_);
static void DdmBroadcast(bool connect) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ static void PostThreadStart(Thread* t)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ static void PostThreadDeath(Thread* t)
+ REQUIRES_SHARED(Locks::mutator_lock_);
static void PostThreadStartOrStop(Thread*, uint32_t)
REQUIRES_SHARED(Locks::mutator_lock_);
+ static void PostClassPrepare(mirror::Class* c)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
static void PostLocationEvent(ArtMethod* method, int pcOffset,
mirror::Object* thisPtr, int eventFlags,
const JValue* return_value)
@@ -789,6 +800,22 @@
static size_t exception_catch_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_);
static uint32_t instrumentation_events_ GUARDED_BY(Locks::mutator_lock_);
+ class DbgThreadLifecycleCallback : public ThreadLifecycleCallback {
+ public:
+ void ThreadStart(Thread* self) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+ void ThreadDeath(Thread* self) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+ };
+
+ class DbgClassLoadCallback : public ClassLoadCallback {
+ public:
+ void ClassLoad(Handle<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+ void ClassPrepare(Handle<mirror::Class> temp_klass,
+ Handle<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+ };
+
+ static DbgThreadLifecycleCallback thread_lifecycle_callback_;
+ static DbgClassLoadCallback class_load_callback_;
+
DISALLOW_COPY_AND_ASSIGN(Dbg);
};
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index b0c4597..7ae9f03 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -160,7 +160,7 @@
// image at GetImageLocation(). This is used for testing mismatched
// image checksums in the oat_file_assistant_tests.
std::string GetImageLocation2() const {
- return GetImageDirectory() + "/core-npic.art";
+ return GetImageDirectory() + "/core-interpreter.art";
}
std::string GetDexSrc1() const {
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 34d8284..268cca0 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -360,7 +360,7 @@
// If we are the zygote, the non moving space becomes the zygote space when we run
// PreZygoteFork the first time. In this case, call the map "zygote space" since we can't
// rename the mem map later.
- const char* space_name = is_zygote ? kZygoteSpaceName: kNonMovingSpaceName;
+ const char* space_name = is_zygote ? kZygoteSpaceName : kNonMovingSpaceName;
// Reserve the non moving mem map before the other two since it needs to be at a specific
// address.
non_moving_space_mem_map.reset(
diff --git a/runtime/gc/reference_processor.h b/runtime/gc/reference_processor.h
index b15544d..38b68cb 100644
--- a/runtime/gc/reference_processor.h
+++ b/runtime/gc/reference_processor.h
@@ -42,7 +42,7 @@
class Heap;
-// Used to process java.lang.References concurrently or paused.
+// Used to process java.lang.ref.Reference instances concurrently or paused.
class ReferenceProcessor {
public:
explicit ReferenceProcessor();
diff --git a/runtime/gc/scoped_gc_critical_section.h b/runtime/gc/scoped_gc_critical_section.h
index ec93bca..1271ff7 100644
--- a/runtime/gc/scoped_gc_critical_section.h
+++ b/runtime/gc/scoped_gc_critical_section.h
@@ -27,8 +27,8 @@
namespace gc {
-// Wait until the GC is finished and then prevent GC from starting until the destructor. Used
-// to prevent deadlocks in places where we call ClassLinker::VisitClass with all th threads
+// Wait until the GC is finished and then prevent the GC from starting until the destructor. Used
+// to prevent deadlocks in places where we call ClassLinker::VisitClass with all the threads
// suspended.
class ScopedGCCriticalSection {
public:
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index e71a397..4c6b5bf 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -141,16 +141,6 @@
return nullptr;
}
mirror::Object* const obj = reinterpret_cast<mirror::Object*>(mem_map->Begin());
- if (kIsDebugBuild) {
- ReaderMutexLock mu2(Thread::Current(), *Locks::heap_bitmap_lock_);
- auto* heap = Runtime::Current()->GetHeap();
- auto* live_bitmap = heap->GetLiveBitmap();
- auto* space_bitmap = live_bitmap->GetContinuousSpaceBitmap(obj);
- CHECK(space_bitmap == nullptr) << obj << " overlaps with bitmap " << *space_bitmap;
- auto* obj_end = reinterpret_cast<mirror::Object*>(mem_map->End());
- space_bitmap = live_bitmap->GetContinuousSpaceBitmap(obj_end - 1);
- CHECK(space_bitmap == nullptr) << obj_end << " overlaps with bitmap " << *space_bitmap;
- }
MutexLock mu(self, lock_);
large_objects_.Put(obj, LargeObject {mem_map, false /* not zygote */});
const size_t allocation_size = mem_map->BaseSize();
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 76777d9..28bcb97 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -596,7 +596,7 @@
// Get the register arguments for the invoke.
inst->GetVarArgs(args, inst_data);
// Drop the first register which is the method handle performing the invoke.
- memcpy(args, args + 1, sizeof(args[0]) * (Instruction::kMaxVarArgRegs - 1));
+ memmove(args, args + 1, sizeof(args[0]) * (Instruction::kMaxVarArgRegs - 1));
args[Instruction::kMaxVarArgRegs - 1] = 0;
return DoInvokePolymorphic<is_range, do_access_check>(self,
invoke_method,
diff --git a/runtime/interpreter/mterp/arm64/op_iput_wide_quick.S b/runtime/interpreter/mterp/arm64/op_iput_wide_quick.S
index 6cec363..28e831a 100644
--- a/runtime/interpreter/mterp/arm64/op_iput_wide_quick.S
+++ b/runtime/interpreter/mterp/arm64/op_iput_wide_quick.S
@@ -4,7 +4,7 @@
GET_VREG w2, w2 // w2<- fp[B], the object pointer
ubfx w0, wINST, #8, #4 // w0<- A
cbz w2, common_errNullObject // object was null
- GET_VREG_WIDE x0, w0 // x0-< fp[A]
+ GET_VREG_WIDE x0, w0 // x0<- fp[A]
FETCH_ADVANCE_INST 2 // advance rPC, load wINST
str x0, [x2, x3] // obj.field<- x0
GET_INST_OPCODE ip // extract opcode from wINST
diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S
index 681790d..7d442c0 100644
--- a/runtime/interpreter/mterp/out/mterp_arm64.S
+++ b/runtime/interpreter/mterp/out/mterp_arm64.S
@@ -6593,7 +6593,7 @@
GET_VREG w2, w2 // w2<- fp[B], the object pointer
ubfx w0, wINST, #8, #4 // w0<- A
cbz w2, common_errNullObject // object was null
- GET_VREG_WIDE x0, w0 // x0-< fp[A]
+ GET_VREG_WIDE x0, w0 // x0<- fp[A]
FETCH_ADVANCE_INST 2 // advance rPC, load wINST
str x0, [x2, x3] // obj.field<- x0
GET_INST_OPCODE ip // extract opcode from wINST
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 7dd3d3d..feb6e08 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -1443,7 +1443,7 @@
ObjPtr<mirror::Object> java_method_obj = shadow_frame->GetVRegReference(arg_offset);
ScopedLocalRef<jobject> java_method(env,
- java_method_obj == nullptr ? nullptr :env->AddLocalReference<jobject>(java_method_obj));
+ java_method_obj == nullptr ? nullptr : env->AddLocalReference<jobject>(java_method_obj));
ObjPtr<mirror::Object> java_receiver_obj = shadow_frame->GetVRegReference(arg_offset + 1);
ScopedLocalRef<jobject> java_receiver(env,
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index b7125a8..6deb03d 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -26,7 +26,7 @@
#include "jit_code_cache.h"
#include "oat_file_manager.h"
#include "oat_quick_method_header.h"
-#include "offline_profiling_info.h"
+#include "profile_compilation_info.h"
#include "profile_saver.h"
#include "runtime.h"
#include "runtime_options.h"
@@ -514,7 +514,7 @@
}
}
- native_pc = stack_map.GetNativePcOffset(encoding.stack_map_encoding) +
+ native_pc = stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA) +
osr_method->GetEntryPoint();
VLOG(jit) << "Jumping to "
<< method_name
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index 05c3905..4112142 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -25,7 +25,7 @@
#include "jit/profile_saver_options.h"
#include "obj_ptr.h"
#include "object_callbacks.h"
-#include "offline_profiling_info.h"
+#include "profile_compilation_info.h"
#include "thread_pool.h"
namespace art {
diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/profile_compilation_info.cc
similarity index 99%
rename from runtime/jit/offline_profiling_info.cc
rename to runtime/jit/profile_compilation_info.cc
index 6f2a8c6..1405c40 100644
--- a/runtime/jit/offline_profiling_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "offline_profiling_info.h"
+#include "profile_compilation_info.h"
#include "errno.h"
#include <limits.h>
diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/profile_compilation_info.h
similarity index 97%
rename from runtime/jit/offline_profiling_info.h
rename to runtime/jit/profile_compilation_info.h
index 53d0eea..f8061bc 100644
--- a/runtime/jit/offline_profiling_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_JIT_OFFLINE_PROFILING_INFO_H_
-#define ART_RUNTIME_JIT_OFFLINE_PROFILING_INFO_H_
+#ifndef ART_RUNTIME_JIT_PROFILE_COMPILATION_INFO_H_
+#define ART_RUNTIME_JIT_PROFILE_COMPILATION_INFO_H_
#include <set>
#include <vector>
@@ -29,7 +29,6 @@
namespace art {
-// TODO: rename file.
/**
* Profile information in a format suitable to be queried by the compiler and
* performing profile guided compilation.
@@ -187,4 +186,4 @@
} // namespace art
-#endif // ART_RUNTIME_JIT_OFFLINE_PROFILING_INFO_H_
+#endif // ART_RUNTIME_JIT_PROFILE_COMPILATION_INFO_H_
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index 1dd1e36..835a5f3 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -25,7 +25,7 @@
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "handle_scope-inl.h"
-#include "jit/offline_profiling_info.h"
+#include "jit/profile_compilation_info.h"
#include "scoped_thread_state_change-inl.h"
namespace art {
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index 59e2c94..9c5e41f 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -19,7 +19,7 @@
#include "base/mutex.h"
#include "jit_code_cache.h"
-#include "offline_profiling_info.h"
+#include "profile_compilation_info.h"
#include "profile_saver_options.h"
#include "safe_map.h"
diff --git a/runtime/mirror/class_ext.cc b/runtime/mirror/class_ext.cc
index 7c6a710..efd949e 100644
--- a/runtime/mirror/class_ext.cc
+++ b/runtime/mirror/class_ext.cc
@@ -113,6 +113,11 @@
}
}
+void ClassExt::SetOriginalDexFileBytes(ObjPtr<ByteArray> bytes) {
+ DCHECK(!Runtime::Current()->IsActiveTransaction());
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_bytes_), bytes);
+}
+
void ClassExt::SetClass(ObjPtr<Class> dalvik_system_ClassExt) {
CHECK(dalvik_system_ClassExt != nullptr);
dalvik_system_ClassExt_ = GcRoot<Class>(dalvik_system_ClassExt);
diff --git a/runtime/mirror/class_ext.h b/runtime/mirror/class_ext.h
index 9104631..ad8a61b 100644
--- a/runtime/mirror/class_ext.h
+++ b/runtime/mirror/class_ext.h
@@ -61,6 +61,12 @@
return GetFieldObject<PointerArray>(OFFSET_OF_OBJECT_MEMBER(ClassExt, obsolete_methods_));
}
+ ByteArray* GetOriginalDexFileBytes() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetFieldObject<ByteArray>(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_bytes_));
+ }
+
+ void SetOriginalDexFileBytes(ObjPtr<ByteArray> bytes) REQUIRES_SHARED(Locks::mutator_lock_);
+
void SetObsoleteArrays(ObjPtr<PointerArray> methods, ObjPtr<ObjectArray<DexCache>> dex_caches)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -80,7 +86,7 @@
HeapReference<PointerArray> obsolete_methods_;
- HeapReference<DexCache> original_dex_cache_;
+ HeapReference<ByteArray> original_dex_file_bytes_;
// The saved verification error of this class.
HeapReference<Object> verify_error_;
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 9c09275..071b0e2 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -1394,6 +1394,12 @@
}
}
+size_t MonitorList::Size() {
+ Thread* self = Thread::Current();
+ MutexLock mu(self, monitor_list_lock_);
+ return list_.size();
+}
+
class MonitorDeflateVisitor : public IsMarkedVisitor {
public:
MonitorDeflateVisitor() : self_(Thread::Current()), deflate_count_(0) {}
diff --git a/runtime/monitor.h b/runtime/monitor.h
index c3da563..1fa4682 100644
--- a/runtime/monitor.h
+++ b/runtime/monitor.h
@@ -331,6 +331,7 @@
void BroadcastForNewMonitors() REQUIRES(!monitor_list_lock_);
// Returns how many monitors were deflated.
size_t DeflateMonitors() REQUIRES(!monitor_list_lock_) REQUIRES(Locks::mutator_lock_);
+ size_t Size() REQUIRES(!monitor_list_lock_);
typedef std::list<Monitor*, TrackingAllocator<Monitor*, kAllocatorTagMonitorList>> Monitors;
diff --git a/runtime/oat.h b/runtime/oat.h
index 3b3ab5a..29821a2 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- static constexpr uint8_t kOatVersion[] = { '1', '0', '1', '\0' }; // Array entrypoints change
+ static constexpr uint8_t kOatVersion[] = { '1', '0', '3', '\0' }; // Native pc change
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index e8e9295..9669dab 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -152,15 +152,17 @@
}
}
- if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) {
- if (relocate) {
- EXPECT_EQ(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
- oat_header.GetImageFileLocationOatDataBegin());
- EXPECT_EQ(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
- } else {
- EXPECT_NE(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
- oat_header.GetImageFileLocationOatDataBegin());
- EXPECT_NE(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
+ if (!with_alternate_image) {
+ if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) {
+ if (relocate) {
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
+ oat_header.GetImageFileLocationOatDataBegin());
+ EXPECT_EQ(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
+ } else {
+ EXPECT_NE(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
+ oat_header.GetImageFileLocationOatDataBegin());
+ EXPECT_NE(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
+ }
}
}
}
diff --git a/runtime/oat_quick_method_header.cc b/runtime/oat_quick_method_header.cc
index 9c2378d..fd84426 100644
--- a/runtime/oat_quick_method_header.cc
+++ b/runtime/oat_quick_method_header.cc
@@ -80,7 +80,7 @@
: code_info.GetStackMapForDexPc(dex_pc, encoding);
if (stack_map.IsValid()) {
return reinterpret_cast<uintptr_t>(entry_point) +
- stack_map.GetNativePcOffset(encoding.stack_map_encoding);
+ stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA);
}
if (abort_on_failure) {
ScopedObjectAccess soa(Thread::Current());
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index d5c6520..976a1e7 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -21,12 +21,15 @@
"object_tagging.cc",
"OpenjdkJvmTi.cc",
"ti_class.cc",
+ "ti_class_definition.cc",
+ "ti_dump.cc",
"ti_field.cc",
"ti_heap.cc",
"ti_jni.cc",
"ti_method.cc",
"ti_monitor.cc",
"ti_object.cc",
+ "ti_phase.cc",
"ti_properties.cc",
"ti_search.cc",
"ti_stack.cc",
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 90467db..417d104 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -49,12 +49,14 @@
#include "thread-inl.h"
#include "thread_list.h"
#include "ti_class.h"
+#include "ti_dump.h"
#include "ti_field.h"
#include "ti_heap.h"
#include "ti_jni.h"
#include "ti_method.h"
#include "ti_monitor.h"
#include "ti_object.h"
+#include "ti_phase.h"
#include "ti_properties.h"
#include "ti_redefine.h"
#include "ti_search.h"
@@ -194,15 +196,15 @@
jvmtiStartFunction proc,
const void* arg,
jint priority) {
- return ERR(NOT_IMPLEMENTED);
+ return ThreadUtil::RunAgentThread(env, thread, proc, arg, priority);
}
static jvmtiError SetThreadLocalStorage(jvmtiEnv* env, jthread thread, const void* data) {
- return ERR(NOT_IMPLEMENTED);
+ return ThreadUtil::SetThreadLocalStorage(env, thread, data);
}
static jvmtiError GetThreadLocalStorage(jvmtiEnv* env, jthread thread, void** data_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ThreadUtil::GetThreadLocalStorage(env, thread, data_ptr);
}
static jvmtiError GetTopThreadGroups(jvmtiEnv* env,
@@ -593,7 +595,7 @@
jclass klass,
jint* minor_version_ptr,
jint* major_version_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ClassUtil::GetClassVersionNumbers(env, klass, minor_version_ptr, major_version_ptr);
}
static jvmtiError GetConstantPool(jvmtiEnv* env,
@@ -631,7 +633,17 @@
}
static jvmtiError RetransformClasses(jvmtiEnv* env, jint class_count, const jclass* classes) {
- return ERR(NOT_IMPLEMENTED);
+ std::string error_msg;
+ jvmtiError res = Transformer::RetransformClasses(ArtJvmTiEnv::AsArtJvmTiEnv(env),
+ art::Runtime::Current(),
+ art::Thread::Current(),
+ class_count,
+ classes,
+ &error_msg);
+ if (res != OK) {
+ LOG(WARNING) << "FAILURE TO RETRANFORM " << error_msg;
+ }
+ return res;
}
static jvmtiError RedefineClasses(jvmtiEnv* env,
@@ -1089,11 +1101,12 @@
}
static jvmtiError GetPhase(jvmtiEnv* env, jvmtiPhase* phase_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return PhaseUtil::GetPhase(env, phase_ptr);
}
static jvmtiError DisposeEnvironment(jvmtiEnv* env) {
ENSURE_VALID_ENV(env);
+ gEventHandler.RemoveArtJvmTiEnv(ArtJvmTiEnv::AsArtJvmTiEnv(env));
delete env;
return OK;
}
@@ -1255,78 +1268,6 @@
*format_ptr = jvmtiJlocationFormat::JVMTI_JLOCATION_JVMBCI;
return ERR(NONE);
}
-
- // TODO Remove this once events are working.
- static jvmtiError RetransformClassWithHook(jvmtiEnv* env,
- jclass klass,
- jvmtiEventClassFileLoadHook hook) {
- std::vector<jclass> classes;
- classes.push_back(klass);
- return RetransformClassesWithHook(reinterpret_cast<ArtJvmTiEnv*>(env), classes, hook);
- }
-
- // TODO This will be called by the event handler for the art::ti Event Load Event
- static jvmtiError RetransformClassesWithHook(ArtJvmTiEnv* env,
- const std::vector<jclass>& classes,
- jvmtiEventClassFileLoadHook hook) {
- if (!IsValidEnv(env)) {
- return ERR(INVALID_ENVIRONMENT);
- }
- jvmtiError res = OK;
- std::string error;
- for (jclass klass : classes) {
- JNIEnv* jni_env = nullptr;
- jobject loader = nullptr;
- std::string name;
- jobject protection_domain = nullptr;
- jint data_len = 0;
- unsigned char* dex_data = nullptr;
- jvmtiError ret = OK;
- std::string location;
- if ((ret = GetTransformationData(env,
- klass,
- /*out*/&location,
- /*out*/&jni_env,
- /*out*/&loader,
- /*out*/&name,
- /*out*/&protection_domain,
- /*out*/&data_len,
- /*out*/&dex_data)) != OK) {
- // TODO Do something more here? Maybe give log statements?
- return ret;
- }
- jint new_data_len = 0;
- unsigned char* new_dex_data = nullptr;
- hook(env,
- jni_env,
- klass,
- loader,
- name.c_str(),
- protection_domain,
- data_len,
- dex_data,
- /*out*/&new_data_len,
- /*out*/&new_dex_data);
- // Check if anything actually changed.
- if ((new_data_len != 0 || new_dex_data != nullptr) && new_dex_data != dex_data) {
- jvmtiClassDefinition def = { klass, new_data_len, new_dex_data };
- res = Redefiner::RedefineClasses(env,
- art::Runtime::Current(),
- art::Thread::Current(),
- 1,
- &def,
- &error);
- env->Deallocate(new_dex_data);
- }
- // Deallocate the old dex data.
- env->Deallocate(dex_data);
- if (res != OK) {
- LOG(ERROR) << "FAILURE TO REDEFINE " << error;
- return res;
- }
- }
- return OK;
- }
};
static bool IsJvmtiVersion(jint version) {
@@ -1362,17 +1303,35 @@
// The plugin initialization function. This adds the jvmti environment.
extern "C" bool ArtPlugin_Initialize() {
art::Runtime* runtime = art::Runtime::Current();
+
+ if (runtime->IsStarted()) {
+ PhaseUtil::SetToLive();
+ } else {
+ PhaseUtil::SetToOnLoad();
+ }
+ PhaseUtil::Register(&gEventHandler);
+ ThreadUtil::Register(&gEventHandler);
+ ClassUtil::Register(&gEventHandler);
+ DumpUtil::Register(&gEventHandler);
+
runtime->GetJavaVM()->AddEnvironmentHook(GetEnvHandler);
runtime->AddSystemWeakHolder(&gObjectTagTable);
+
+ return true;
+}
+
+extern "C" bool ArtPlugin_Deinitialize() {
+ PhaseUtil::Unregister();
+ ThreadUtil::Unregister();
+ ClassUtil::Unregister();
+ DumpUtil::Unregister();
+
return true;
}
// The actual struct holding all of the entrypoints into the jvmti interface.
const jvmtiInterface_1 gJvmtiInterface = {
- // SPECIAL FUNCTION: RetransformClassWithHook Is normally reserved1
- // TODO Remove once we have events working.
- reinterpret_cast<void*>(JvmtiFunctions::RetransformClassWithHook),
- // nullptr, // reserved1
+ nullptr, // reserved1
JvmtiFunctions::SetEventNotificationMode,
nullptr, // reserved3
JvmtiFunctions::GetAllThreads,
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index 5eadc5a..256c3a6 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -36,6 +36,7 @@
#include <jni.h>
+#include "base/array_slice.h"
#include "base/casts.h"
#include "base/logging.h"
#include "base/macros.h"
@@ -47,6 +48,7 @@
namespace openjdkjvmti {
extern const jvmtiInterface_1 gJvmtiInterface;
+extern EventHandler gEventHandler;
// A structure that is a jvmtiEnv with additional information for the runtime.
struct ArtJvmTiEnv : public jvmtiEnv {
@@ -134,7 +136,7 @@
.can_get_current_contended_monitor = 0,
.can_get_monitor_info = 0,
.can_pop_frame = 0,
- .can_redefine_classes = 0,
+ .can_redefine_classes = 1,
.can_signal_thread = 0,
.can_get_source_file_name = 0,
.can_get_line_numbers = 0,
@@ -162,7 +164,7 @@
.can_get_owned_monitor_stack_depth_info = 0,
.can_get_constant_pool = 0,
.can_set_native_method_prefix = 0,
- .can_retransform_classes = 0,
+ .can_retransform_classes = 1,
.can_retransform_any_class = 0,
.can_generate_resource_exhaustion_heap_events = 0,
.can_generate_resource_exhaustion_threads_events = 0,
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index 1e07bc6..21ec731 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -115,9 +115,95 @@
}
template <typename ...Args>
+inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread*,
+ ArtJvmtiEvent event,
+ Args... args ATTRIBUTE_UNUSED) const {
+ CHECK(event == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
+ event == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
+ LOG(FATAL) << "Incorrect arguments to ClassFileLoadHook!";
+}
+
+// TODO Locking of some type!
+template <>
+inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread,
+ ArtJvmtiEvent event,
+ JNIEnv* jnienv,
+ jclass class_being_redefined,
+ jobject loader,
+ const char* name,
+ jobject protection_domain,
+ jint class_data_len,
+ const unsigned char* class_data,
+ jint* new_class_data_len,
+ unsigned char** new_class_data) const {
+ CHECK(event == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
+ event == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
+ using FnType = void(jvmtiEnv* /* jvmti_env */,
+ JNIEnv* /* jnienv */,
+ jclass /* class_being_redefined */,
+ jobject /* loader */,
+ const char* /* name */,
+ jobject /* protection_domain */,
+ jint /* class_data_len */,
+ const unsigned char* /* class_data */,
+ jint* /* new_class_data_len */,
+ unsigned char** /* new_class_data */);
+ jint current_len = class_data_len;
+ unsigned char* current_class_data = const_cast<unsigned char*>(class_data);
+ ArtJvmTiEnv* last_env = nullptr;
+ for (ArtJvmTiEnv* env : envs) {
+ if (ShouldDispatch(event, env, thread)) {
+ jint new_len;
+ unsigned char* new_data;
+ FnType* callback = GetCallback<FnType>(env, event);
+ callback(env,
+ jnienv,
+ class_being_redefined,
+ loader,
+ name,
+ protection_domain,
+ current_len,
+ current_class_data,
+ &new_len,
+ &new_data);
+ if (new_data != nullptr && new_data != current_class_data) {
+ // Destroy the data the last transformer made. We skip this if the previous state was the
+ // initial one since we don't know here which jvmtiEnv allocated it.
+ // NB Currently this doesn't matter since all allocations just go to malloc but in the
+ // future we might have jvmtiEnv's keep track of their allocations for leak-checking.
+ if (last_env != nullptr) {
+ last_env->Deallocate(current_class_data);
+ }
+ last_env = env;
+ current_class_data = new_data;
+ current_len = new_len;
+ }
+ }
+ }
+ if (last_env != nullptr) {
+ *new_class_data_len = current_len;
+ *new_class_data = current_class_data;
+ }
+}
+
+template <typename ...Args>
inline void EventHandler::DispatchEvent(art::Thread* thread,
ArtJvmtiEvent event,
Args... args) const {
+ switch (event) {
+ case ArtJvmtiEvent::kClassFileLoadHookRetransformable:
+ case ArtJvmtiEvent::kClassFileLoadHookNonRetransformable:
+ return DispatchClassFileLoadHookEvent(thread, event, args...);
+ default:
+ return GenericDispatchEvent(thread, event, args...);
+ }
+}
+
+// TODO Locking of some type!
+template <typename ...Args>
+inline void EventHandler::GenericDispatchEvent(art::Thread* thread,
+ ArtJvmtiEvent event,
+ Args... args) const {
using FnType = void(jvmtiEnv*, Args...);
for (ArtJvmTiEnv* env : envs) {
if (ShouldDispatch(event, env, thread)) {
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index f38aa86..d3f8001 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -144,6 +144,18 @@
envs.push_back(env);
}
+void EventHandler::RemoveArtJvmTiEnv(ArtJvmTiEnv* env) {
+ auto it = std::find(envs.begin(), envs.end(), env);
+ if (it != envs.end()) {
+ envs.erase(it);
+ for (size_t i = static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal);
+ i <= static_cast<size_t>(ArtJvmtiEvent::kMaxEventTypeVal);
+ ++i) {
+ RecalculateGlobalEventMask(static_cast<ArtJvmtiEvent>(i));
+ }
+ }
+}
+
static bool IsThreadControllable(ArtJvmtiEvent event) {
switch (event) {
case ArtJvmtiEvent::kVmInit:
@@ -200,7 +212,7 @@
thread.get(),
object.get(),
klass.get(),
- byte_count);
+ static_cast<jlong>(byte_count));
}
}
diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h
index 7990141..8e246de 100644
--- a/runtime/openjdkjvmti/events.h
+++ b/runtime/openjdkjvmti/events.h
@@ -141,6 +141,9 @@
// enabled, yet.
void RegisterArtJvmTiEnv(ArtJvmTiEnv* env);
+ // Remove an env.
+ void RemoveArtJvmTiEnv(ArtJvmTiEnv* env);
+
bool IsEventEnabledAnywhere(ArtJvmtiEvent event) const {
if (!EventMask::EventIsInRange(event)) {
return false;
@@ -178,6 +181,15 @@
ALWAYS_INLINE
inline void RecalculateGlobalEventMask(ArtJvmtiEvent event);
+ template <typename ...Args>
+ ALWAYS_INLINE inline void GenericDispatchEvent(art::Thread* thread,
+ ArtJvmtiEvent event,
+ Args... args) const;
+ template <typename ...Args>
+ ALWAYS_INLINE inline void DispatchClassFileLoadHookEvent(art::Thread* thread,
+ ArtJvmtiEvent event,
+ Args... args) const;
+
void HandleEventType(ArtJvmtiEvent event, bool enable);
// List of all JvmTiEnv objects that have been created, in their creation order.
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index d1324bc..450f75a 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -31,16 +31,119 @@
#include "ti_class.h"
+#include <mutex>
+#include <unordered_set>
+
#include "art_jvmti.h"
+#include "base/macros.h"
#include "class_table-inl.h"
#include "class_linker.h"
+#include "events-inl.h"
+#include "handle.h"
+#include "jni_env_ext-inl.h"
#include "jni_internal.h"
#include "runtime.h"
+#include "runtime_callbacks.h"
+#include "ScopedLocalRef.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-inl.h"
+#include "thread_list.h"
namespace openjdkjvmti {
+struct ClassCallback : public art::ClassLoadCallback {
+ void ClassLoad(art::Handle<art::mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassLoad)) {
+ art::Thread* thread = art::Thread::Current();
+ ScopedLocalRef<jclass> jklass(thread->GetJniEnv(),
+ thread->GetJniEnv()->AddLocalReference<jclass>(klass.Get()));
+ ScopedLocalRef<jclass> jthread(
+ thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jclass>(thread->GetPeer()));
+ {
+ art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
+ event_handler->DispatchEvent(thread,
+ ArtJvmtiEvent::kClassLoad,
+ reinterpret_cast<JNIEnv*>(thread->GetJniEnv()),
+ jthread.get(),
+ jklass.get());
+ }
+ AddTempClass(thread, jklass.get());
+ }
+ }
+
+ void ClassPrepare(art::Handle<art::mirror::Class> temp_klass ATTRIBUTE_UNUSED,
+ art::Handle<art::mirror::Class> klass)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassPrepare)) {
+ art::Thread* thread = art::Thread::Current();
+ ScopedLocalRef<jclass> jklass(thread->GetJniEnv(),
+ thread->GetJniEnv()->AddLocalReference<jclass>(klass.Get()));
+ ScopedLocalRef<jclass> jthread(
+ thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jclass>(thread->GetPeer()));
+ art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
+ event_handler->DispatchEvent(thread,
+ ArtJvmtiEvent::kClassPrepare,
+ reinterpret_cast<JNIEnv*>(thread->GetJniEnv()),
+ jthread.get(),
+ jklass.get());
+ }
+ }
+
+ void AddTempClass(art::Thread* self, jclass klass) {
+ std::unique_lock<std::mutex> mu(temp_classes_lock);
+ temp_classes.push_back(reinterpret_cast<jclass>(self->GetJniEnv()->NewGlobalRef(klass)));
+ }
+
+ void HandleTempClass(art::Handle<art::mirror::Class> temp_klass,
+ art::Handle<art::mirror::Class> klass)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ std::unique_lock<std::mutex> mu(temp_classes_lock);
+ if (temp_classes.empty()) {
+ return;
+ }
+
+ art::Thread* self = art::Thread::Current();
+ for (auto it = temp_classes.begin(); it != temp_classes.end(); ++it) {
+ if (temp_klass.Get() == art::ObjPtr<art::mirror::Class>::DownCast(self->DecodeJObject(*it))) {
+ temp_classes.erase(it);
+ FixupTempClass(temp_klass, klass);
+ }
+ }
+ }
+
+ void FixupTempClass(art::Handle<art::mirror::Class> temp_klass ATTRIBUTE_UNUSED,
+ art::Handle<art::mirror::Class> klass ATTRIBUTE_UNUSED)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ // TODO: Implement.
+ }
+
+ // A set of all the temp classes we have handed out. We have to fix up references to these.
+ // For simplicity, we store the temp classes as JNI global references in a vector. Normally a
+ // Prepare event will closely follow, so the vector should be small.
+ std::mutex temp_classes_lock;
+ std::vector<jclass> temp_classes;
+
+ EventHandler* event_handler = nullptr;
+};
+
+ClassCallback gClassCallback;
+
+void ClassUtil::Register(EventHandler* handler) {
+ gClassCallback.event_handler = handler;
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Add load callback");
+ art::Runtime::Current()->GetRuntimeCallbacks()->AddClassLoadCallback(&gClassCallback);
+}
+
+void ClassUtil::Unregister() {
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Remove thread callback");
+ art::Runtime* runtime = art::Runtime::Current();
+ runtime->GetRuntimeCallbacks()->RemoveClassLoadCallback(&gClassCallback);
+}
+
jvmtiError ClassUtil::GetClassFields(jvmtiEnv* env,
jclass jklass,
jint* field_count_ptr,
@@ -200,7 +303,9 @@
}
// TODO: Support generic signature.
- *generic_ptr = nullptr;
+ if (generic_ptr != nullptr) {
+ *generic_ptr = nullptr;
+ }
// Everything is fine, release the buffers.
sig_copy.release();
@@ -417,4 +522,35 @@
return ERR(NONE);
}
+jvmtiError ClassUtil::GetClassVersionNumbers(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jclass jklass,
+ jint* minor_version_ptr,
+ jint* major_version_ptr) {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ if (jklass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+ art::ObjPtr<art::mirror::Object> jklass_obj = soa.Decode<art::mirror::Object>(jklass);
+ if (!jklass_obj->IsClass()) {
+ return ERR(INVALID_CLASS);
+ }
+ art::ObjPtr<art::mirror::Class> klass = jklass_obj->AsClass();
+ if (klass->IsPrimitive() || klass->IsArrayClass()) {
+ return ERR(INVALID_CLASS);
+ }
+
+ if (minor_version_ptr == nullptr || major_version_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ // Note: proxies will show the dex file version of java.lang.reflect.Proxy, as that is
+ // what their dex cache copies from.
+ uint32_t version = klass->GetDexFile().GetHeader().GetVersion();
+
+ *major_version_ptr = static_cast<jint>(version);
+ *minor_version_ptr = 0;
+
+ return ERR(NONE);
+}
+
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_class.h b/runtime/openjdkjvmti/ti_class.h
index 7a0fafb..aa2260f 100644
--- a/runtime/openjdkjvmti/ti_class.h
+++ b/runtime/openjdkjvmti/ti_class.h
@@ -37,8 +37,13 @@
namespace openjdkjvmti {
+class EventHandler;
+
class ClassUtil {
public:
+ static void Register(EventHandler* event_handler);
+ static void Unregister();
+
static jvmtiError GetClassFields(jvmtiEnv* env,
jclass klass,
jint* field_count_ptr,
@@ -72,6 +77,11 @@
static jvmtiError IsInterface(jvmtiEnv* env, jclass klass, jboolean* is_interface_ptr);
static jvmtiError IsArrayClass(jvmtiEnv* env, jclass klass, jboolean* is_array_class_ptr);
+
+ static jvmtiError GetClassVersionNumbers(jvmtiEnv* env,
+ jclass klass,
+ jint* minor_version_ptr,
+ jint* major_version_ptr);
};
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_class_definition.cc b/runtime/openjdkjvmti/ti_class_definition.cc
new file mode 100644
index 0000000..2c2a79b
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_class_definition.cc
@@ -0,0 +1,55 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_class_definition.h"
+
+#include "dex_file.h"
+#include "handle_scope-inl.h"
+#include "handle.h"
+#include "mirror/class-inl.h"
+#include "mirror/object-inl.h"
+#include "thread.h"
+
+namespace openjdkjvmti {
+
+bool ArtClassDefinition::IsModified(art::Thread* self) const {
+ if (modified) {
+ return true;
+ }
+ // Check if the dex file we want to set is the same as the current one.
+ art::StackHandleScope<1> hs(self);
+ art::Handle<art::mirror::Class> h_klass(hs.NewHandle(self->DecodeJObject(klass)->AsClass()));
+ const art::DexFile& cur_dex_file = h_klass->GetDexFile();
+ return static_cast<jint>(cur_dex_file.Size()) != dex_len ||
+ memcmp(cur_dex_file.Begin(), dex_data.get(), dex_len) != 0;
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_class_definition.h b/runtime/openjdkjvmti/ti_class_definition.h
new file mode 100644
index 0000000..dbe5da2
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_class_definition.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_DEFINITION_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_DEFINITION_H_
+
+#include "art_jvmti.h"
+
+namespace openjdkjvmti {
+
+// A struct that stores data needed for redefining/transforming classes. This structure should only
+// even be accessed from a single thread and must not survive past the completion of the
+// redefinition/retransformation function that created it.
+struct ArtClassDefinition {
+ public:
+ jclass klass;
+ jobject loader;
+ std::string name;
+ jobject protection_domain;
+ jint dex_len;
+ JvmtiUniquePtr dex_data;
+ art::ArraySlice<const unsigned char> original_dex_file;
+
+ ArtClassDefinition() = default;
+ ArtClassDefinition(ArtClassDefinition&& o) = default;
+
+ void SetNewDexData(ArtJvmTiEnv* env, jint new_dex_len, unsigned char* new_dex_data) {
+ if (new_dex_data == nullptr) {
+ return;
+ } else if (new_dex_data != dex_data.get() || new_dex_len != dex_len) {
+ SetModified();
+ dex_len = new_dex_len;
+ dex_data = MakeJvmtiUniquePtr(env, new_dex_data);
+ }
+ }
+
+ void SetModified() {
+ modified = true;
+ }
+
+ bool IsModified(art::Thread* self) const REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ private:
+ bool modified;
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_DEFINITION_H_
diff --git a/runtime/openjdkjvmti/ti_dump.cc b/runtime/openjdkjvmti/ti_dump.cc
new file mode 100644
index 0000000..2ee5c40
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_dump.cc
@@ -0,0 +1,74 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_dump.h"
+
+#include <limits>
+
+
+#include "art_jvmti.h"
+#include "base/mutex.h"
+#include "events-inl.h"
+#include "runtime_callbacks.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
+#include "thread_list.h"
+
+namespace openjdkjvmti {
+
+struct DumpCallback : public art::RuntimeSigQuitCallback {
+ void SigQuit() OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::Thread* thread = art::Thread::Current();
+ art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
+ event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kDataDumpRequest);
+ }
+
+ EventHandler* event_handler = nullptr;
+};
+
+static DumpCallback gDumpCallback;
+
+void DumpUtil::Register(EventHandler* handler) {
+ gDumpCallback.event_handler = handler;
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Add sigquit callback");
+ art::Runtime::Current()->GetRuntimeCallbacks()->AddRuntimeSigQuitCallback(&gDumpCallback);
+}
+
+void DumpUtil::Unregister() {
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Remove sigquit callback");
+ art::Runtime::Current()->GetRuntimeCallbacks()->RemoveRuntimeSigQuitCallback(&gDumpCallback);
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_dump.h b/runtime/openjdkjvmti/ti_dump.h
new file mode 100644
index 0000000..67cb239
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_dump.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_DUMP_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_DUMP_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class EventHandler;
+
+class DumpUtil {
+ public:
+ static void Register(EventHandler* event_handler);
+ static void Unregister();
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_DUMP_H_
diff --git a/runtime/openjdkjvmti/ti_phase.cc b/runtime/openjdkjvmti/ti_phase.cc
new file mode 100644
index 0000000..154406a
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_phase.cc
@@ -0,0 +1,142 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_phase.h"
+
+#include "art_jvmti.h"
+#include "base/macros.h"
+#include "events-inl.h"
+#include "runtime.h"
+#include "runtime_callbacks.h"
+#include "ScopedLocalRef.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
+#include "thread_list.h"
+
+namespace openjdkjvmti {
+
+jvmtiPhase PhaseUtil::current_phase_ = static_cast<jvmtiPhase>(0);
+
+struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback {
+ inline static JNIEnv* GetJniEnv() {
+ return reinterpret_cast<JNIEnv*>(art::Thread::Current()->GetJniEnv());
+ }
+
+ inline static jthread GetCurrentJThread() {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ return soa.AddLocalReference<jthread>(soa.Self()->GetPeer());
+ }
+
+ void NextRuntimePhase(RuntimePhase phase) REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE {
+ // TODO: Events.
+ switch (phase) {
+ case RuntimePhase::kInitialAgents:
+ PhaseUtil::current_phase_ = JVMTI_PHASE_PRIMORDIAL;
+ break;
+ case RuntimePhase::kStart:
+ {
+ art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative);
+ event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kVmStart, GetJniEnv());
+ PhaseUtil::current_phase_ = JVMTI_PHASE_START;
+ }
+ break;
+ case RuntimePhase::kInit:
+ {
+ ScopedLocalRef<jthread> thread(GetJniEnv(), GetCurrentJThread());
+ art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative);
+ event_handler->DispatchEvent(nullptr,
+ ArtJvmtiEvent::kVmInit,
+ GetJniEnv(),
+ thread.get());
+ PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE;
+ }
+ break;
+ case RuntimePhase::kDeath:
+ {
+ art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative);
+ event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kVmDeath, GetJniEnv());
+ PhaseUtil::current_phase_ = JVMTI_PHASE_DEAD;
+ }
+ // TODO: Block events now.
+ break;
+ }
+ }
+
+ EventHandler* event_handler = nullptr;
+};
+
+PhaseUtil::PhaseCallback gPhaseCallback;
+
+jvmtiError PhaseUtil::GetPhase(jvmtiEnv* env ATTRIBUTE_UNUSED, jvmtiPhase* phase_ptr) {
+ if (phase_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+ jvmtiPhase now = PhaseUtil::current_phase_;
+ DCHECK(now == JVMTI_PHASE_ONLOAD ||
+ now == JVMTI_PHASE_PRIMORDIAL ||
+ now == JVMTI_PHASE_START ||
+ now == JVMTI_PHASE_LIVE ||
+ now == JVMTI_PHASE_DEAD);
+ *phase_ptr = now;
+ return ERR(NONE);
+}
+
+void PhaseUtil::SetToOnLoad() {
+ DCHECK_EQ(0u, static_cast<size_t>(PhaseUtil::current_phase_));
+ PhaseUtil::current_phase_ = JVMTI_PHASE_ONLOAD;
+}
+
+void PhaseUtil::SetToPrimordial() {
+ DCHECK_EQ(static_cast<size_t>(JVMTI_PHASE_ONLOAD), static_cast<size_t>(PhaseUtil::current_phase_));
+ PhaseUtil::current_phase_ = JVMTI_PHASE_ONLOAD;
+}
+
+void PhaseUtil::SetToLive() {
+ DCHECK_EQ(static_cast<size_t>(0), static_cast<size_t>(PhaseUtil::current_phase_));
+ PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE;
+}
+
+void PhaseUtil::Register(EventHandler* handler) {
+ gPhaseCallback.event_handler = handler;
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Add phase callback");
+ art::Runtime::Current()->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&gPhaseCallback);
+}
+
+void PhaseUtil::Unregister() {
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Remove phase callback");
+ art::Runtime::Current()->GetRuntimeCallbacks()->RemoveRuntimePhaseCallback(&gPhaseCallback);
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_phase.h b/runtime/openjdkjvmti/ti_phase.h
new file mode 100644
index 0000000..bd15fa6
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_phase.h
@@ -0,0 +1,66 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_PHASE_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_PHASE_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class EventHandler;
+
+class PhaseUtil {
+ public:
+ static jvmtiError GetPhase(jvmtiEnv* env, jvmtiPhase* phase_ptr);
+
+ static void Register(EventHandler* event_handler);
+ static void Unregister();
+
+ // Move the phase from unitialized to LOAD.
+ static void SetToOnLoad();
+
+ // Move the phase from LOAD to PRIMORDIAL.
+ static void SetToPrimordial();
+
+ // Move the phase from unitialized to LIVE.
+ static void SetToLive();
+
+ struct PhaseCallback;
+
+ private:
+ static jvmtiPhase current_phase_;
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_PHASE_H_
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 6af51c4..34efc50 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -36,6 +36,7 @@
#include "android-base/stringprintf.h"
#include "art_jvmti.h"
+#include "base/array_slice.h"
#include "base/logging.h"
#include "dex_file.h"
#include "dex_file_types.h"
@@ -228,11 +229,17 @@
return map;
}
-Redefiner::ClassRedefinition::ClassRedefinition(Redefiner* driver,
- jclass klass,
- const art::DexFile* redefined_dex_file,
- const char* class_sig) :
- driver_(driver), klass_(klass), dex_file_(redefined_dex_file), class_sig_(class_sig) {
+Redefiner::ClassRedefinition::ClassRedefinition(
+ Redefiner* driver,
+ jclass klass,
+ const art::DexFile* redefined_dex_file,
+ const char* class_sig,
+ art::ArraySlice<const unsigned char> orig_dex_file) :
+ driver_(driver),
+ klass_(klass),
+ dex_file_(redefined_dex_file),
+ class_sig_(class_sig),
+ original_dex_file_(orig_dex_file) {
GetMirrorClass()->MonitorEnter(driver_->self_);
}
@@ -242,14 +249,12 @@
}
}
-// TODO This should handle doing multiple classes at once so we need to do less cleanup when things
-// go wrong.
jvmtiError Redefiner::RedefineClasses(ArtJvmTiEnv* env,
art::Runtime* runtime,
art::Thread* self,
jint class_count,
const jvmtiClassDefinition* definitions,
- std::string* error_msg) {
+ /*out*/std::string* error_msg) {
if (env == nullptr) {
*error_msg = "env was null!";
return ERR(INVALID_ENVIRONMENT);
@@ -263,46 +268,95 @@
*error_msg = "null definitions!";
return ERR(NULL_POINTER);
}
+ std::vector<ArtClassDefinition> def_vector;
+ def_vector.reserve(class_count);
+ for (jint i = 0; i < class_count; i++) {
+ // We make a copy of the class_bytes to pass into the retransformation.
+ // This makes cleanup easier (since we unambiguously own the bytes) and also is useful since we
+ // will need to keep the original bytes around unaltered for subsequent RetransformClasses calls
+ // to get the passed in bytes.
+ // TODO Implement saving the original bytes.
+ unsigned char* class_bytes_copy = nullptr;
+ jvmtiError res = env->Allocate(definitions[i].class_byte_count, &class_bytes_copy);
+ if (res != OK) {
+ return res;
+ }
+ memcpy(class_bytes_copy, definitions[i].class_bytes, definitions[i].class_byte_count);
+
+ ArtClassDefinition def;
+ def.dex_len = definitions[i].class_byte_count;
+ def.dex_data = MakeJvmtiUniquePtr(env, class_bytes_copy);
+ // We are definitely modified.
+ def.SetModified();
+ def.original_dex_file = art::ArraySlice<const unsigned char>(definitions[i].class_bytes,
+ definitions[i].class_byte_count);
+ res = Transformer::FillInTransformationData(env, definitions[i].klass, &def);
+ if (res != OK) {
+ return res;
+ }
+ def_vector.push_back(std::move(def));
+ }
+ // Call all the transformation events.
+ jvmtiError res = Transformer::RetransformClassesDirect(env,
+ self,
+ &def_vector);
+ if (res != OK) {
+ // Something went wrong with transformation!
+ return res;
+ }
+ return RedefineClassesDirect(env, runtime, self, def_vector, error_msg);
+}
+
+jvmtiError Redefiner::RedefineClassesDirect(ArtJvmTiEnv* env,
+ art::Runtime* runtime,
+ art::Thread* self,
+ const std::vector<ArtClassDefinition>& definitions,
+ std::string* error_msg) {
+ DCHECK(env != nullptr);
+ if (definitions.size() == 0) {
+ // We don't actually need to do anything. Just return OK.
+ return OK;
+ }
// Stop JIT for the duration of this redefine since the JIT might concurrently compile a method we
// are going to redefine.
art::jit::ScopedJitSuspend suspend_jit;
// Get shared mutator lock so we can lock all the classes.
art::ScopedObjectAccess soa(self);
- std::vector<Redefiner::ClassRedefinition> redefinitions;
- redefinitions.reserve(class_count);
Redefiner r(runtime, self, error_msg);
- for (jint i = 0; i < class_count; i++) {
- jvmtiError res = r.AddRedefinition(env, definitions[i]);
- if (res != OK) {
- return res;
+ for (const ArtClassDefinition& def : definitions) {
+ // Only try to transform classes that have been modified.
+ if (def.IsModified(self)) {
+ jvmtiError res = r.AddRedefinition(env, def);
+ if (res != OK) {
+ return res;
+ }
}
}
return r.Run();
}
-jvmtiError Redefiner::AddRedefinition(ArtJvmTiEnv* env, const jvmtiClassDefinition& def) {
+jvmtiError Redefiner::AddRedefinition(ArtJvmTiEnv* env, const ArtClassDefinition& def) {
std::string original_dex_location;
jvmtiError ret = OK;
if ((ret = GetClassLocation(env, def.klass, &original_dex_location))) {
*error_msg_ = "Unable to get original dex file location!";
return ret;
}
- std::unique_ptr<art::MemMap> map(MoveDataToMemMap(original_dex_location,
- def.class_byte_count,
- def.class_bytes,
- error_msg_));
- std::ostringstream os;
char* generic_ptr_unused = nullptr;
char* signature_ptr = nullptr;
- if (env->GetClassSignature(def.klass, &signature_ptr, &generic_ptr_unused) != OK) {
- *error_msg_ = "A jclass passed in does not seem to be valid";
- return ERR(INVALID_CLASS);
+ if ((ret = env->GetClassSignature(def.klass, &signature_ptr, &generic_ptr_unused)) != OK) {
+ *error_msg_ = "Unable to get class signature!";
+ return ret;
}
- // These will make sure we deallocate the signature.
- JvmtiUniquePtr sig_unique_ptr(MakeJvmtiUniquePtr(env, signature_ptr));
JvmtiUniquePtr generic_unique_ptr(MakeJvmtiUniquePtr(env, generic_ptr_unused));
+ JvmtiUniquePtr signature_unique_ptr(MakeJvmtiUniquePtr(env, signature_ptr));
+ std::unique_ptr<art::MemMap> map(MoveDataToMemMap(original_dex_location,
+ def.dex_len,
+ def.dex_data.get(),
+ error_msg_));
+ std::ostringstream os;
if (map.get() == nullptr) {
- os << "Failed to create anonymous mmap for modified dex file of class " << signature_ptr
+ os << "Failed to create anonymous mmap for modified dex file of class " << def.name
<< "in dex file " << original_dex_location << " because: " << *error_msg_;
*error_msg_ = os.str();
return ERR(OUT_OF_MEMORY);
@@ -319,12 +373,16 @@
/*verify_checksum*/true,
error_msg_));
if (dex_file.get() == nullptr) {
- os << "Unable to load modified dex file for " << signature_ptr << ": " << *error_msg_;
+ os << "Unable to load modified dex file for " << def.name << ": " << *error_msg_;
*error_msg_ = os.str();
return ERR(INVALID_CLASS_FORMAT);
}
redefinitions_.push_back(
- Redefiner::ClassRedefinition(this, def.klass, dex_file.release(), signature_ptr));
+ Redefiner::ClassRedefinition(this,
+ def.klass,
+ dex_file.release(),
+ signature_ptr,
+ def.original_dex_file));
return OK;
}
@@ -462,44 +520,48 @@
result_ = result;
}
-bool Redefiner::ClassRedefinition::FinishRemainingAllocations(
- /*out*/art::MutableHandle<art::mirror::ClassLoader>* source_class_loader,
- /*out*/art::MutableHandle<art::mirror::Object>* java_dex_file_obj,
- /*out*/art::MutableHandle<art::mirror::LongArray>* new_dex_file_cookie,
- /*out*/art::MutableHandle<art::mirror::DexCache>* new_dex_cache) {
- art::StackHandleScope<4> hs(driver_->self_);
- // This shouldn't allocate
- art::Handle<art::mirror::ClassLoader> loader(hs.NewHandle(GetClassLoader()));
- if (loader.Get() == nullptr) {
- // TODO Better error msg.
- RecordFailure(ERR(INTERNAL), "Unable to find class loader!");
- return false;
+// Allocates a ByteArray big enough to store the given number of bytes and copies them from the
+// bytes pointer.
+static art::mirror::ByteArray* AllocateAndFillBytes(art::Thread* self,
+ const uint8_t* bytes,
+ int32_t num_bytes)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::StackHandleScope<1> hs(self);
+ art::Handle<art::mirror::ByteArray> arr(hs.NewHandle(
+ art::mirror::ByteArray::Alloc(self, num_bytes)));
+ if (!arr.IsNull()) {
+ // Copy it in. Just skip if it's null
+ memcpy(arr->GetData(), bytes, num_bytes);
}
- art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle(FindSourceDexFileObject(loader)));
- if (dex_file_obj.Get() == nullptr) {
- // TODO Better error msg.
- RecordFailure(ERR(INTERNAL), "Unable to find class loader!");
- return false;
+ return arr.Get();
+}
+
+art::mirror::ByteArray* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFileBytes() {
+ // If we have been specifically given a new set of bytes use that
+ if (original_dex_file_.size() != 0) {
+ return AllocateAndFillBytes(driver_->self_,
+ &original_dex_file_.At(0),
+ original_dex_file_.size());
}
- art::Handle<art::mirror::LongArray> new_cookie(hs.NewHandle(AllocateDexFileCookie(dex_file_obj)));
- if (new_cookie.Get() == nullptr) {
- driver_->self_->AssertPendingOOMException();
- driver_->self_->ClearException();
- RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate dex file array for class loader");
- return false;
+
+ // See if we already have one set.
+ art::ObjPtr<art::mirror::ClassExt> ext(GetMirrorClass()->GetExtData());
+ if (!ext.IsNull()) {
+ art::ObjPtr<art::mirror::ByteArray> old_original_bytes(ext->GetOriginalDexFileBytes());
+ if (!old_original_bytes.IsNull()) {
+ // We do. Use it.
+ return old_original_bytes.Ptr();
+ }
}
- art::Handle<art::mirror::DexCache> dex_cache(hs.NewHandle(CreateNewDexCache(loader)));
- if (dex_cache.Get() == nullptr) {
- driver_->self_->AssertPendingOOMException();
- driver_->self_->ClearException();
- RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate DexCache");
- return false;
+
+ // Copy the current dex_file
+ const art::DexFile& current_dex_file = GetMirrorClass()->GetDexFile();
+ // TODO Handle this or make it so it cannot happen.
+ if (current_dex_file.NumClassDefs() != 1) {
+ LOG(WARNING) << "Current dex file has more than one class in it. Calling RetransformClasses "
+ << "on this class might fail if no transformations are applied to it!";
}
- source_class_loader->Assign(loader.Get());
- java_dex_file_obj->Assign(dex_file_obj.Get());
- new_dex_file_cookie->Assign(new_cookie.Get());
- new_dex_cache->Assign(dex_cache.Get());
- return true;
+ return AllocateAndFillBytes(driver_->self_, current_dex_file.Begin(), current_dex_file.Size());
}
struct CallbackCtx {
@@ -694,9 +756,10 @@
kSlotNewDexFileCookie = 2,
kSlotNewDexCache = 3,
kSlotMirrorClass = 4,
+ kSlotOrigDexFile = 5,
// Must be last one.
- kNumSlots = 5,
+ kNumSlots = 6,
};
// This needs to have a HandleScope passed in that is capable of creating a new Handle without
@@ -737,6 +800,11 @@
return art::down_cast<art::mirror::Class*>(GetSlot(klass_index, kSlotMirrorClass));
}
+ art::mirror::ByteArray* GetOriginalDexFileBytes(jint klass_index)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ return art::down_cast<art::mirror::ByteArray*>(GetSlot(klass_index, kSlotOrigDexFile));
+ }
+
void SetSourceClassLoader(jint klass_index, art::mirror::ClassLoader* loader)
REQUIRES_SHARED(art::Locks::mutator_lock_) {
SetSlot(klass_index, kSlotSourceClassLoader, loader);
@@ -757,6 +825,10 @@
REQUIRES_SHARED(art::Locks::mutator_lock_) {
SetSlot(klass_index, kSlotMirrorClass, klass);
}
+ void SetOriginalDexFileBytes(jint klass_index, art::mirror::ByteArray* bytes)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ SetSlot(klass_index, kSlotOrigDexFile, bytes);
+ }
int32_t Length() REQUIRES_SHARED(art::Locks::mutator_lock_) {
return arr_->GetLength() / kNumSlots;
@@ -782,6 +854,51 @@
DISALLOW_COPY_AND_ASSIGN(RedefinitionDataHolder);
};
+bool Redefiner::ClassRedefinition::FinishRemainingAllocations(
+ int32_t klass_index, /*out*/RedefinitionDataHolder* holder) {
+ art::StackHandleScope<2> hs(driver_->self_);
+ holder->SetMirrorClass(klass_index, GetMirrorClass());
+ // This shouldn't allocate
+ art::Handle<art::mirror::ClassLoader> loader(hs.NewHandle(GetClassLoader()));
+ holder->SetSourceClassLoader(klass_index, loader.Get());
+ if (loader.Get() == nullptr) {
+ // TODO Better error msg.
+ RecordFailure(ERR(INTERNAL), "Unable to find class loader!");
+ return false;
+ }
+ art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle(FindSourceDexFileObject(loader)));
+ holder->SetJavaDexFile(klass_index, dex_file_obj.Get());
+ if (dex_file_obj.Get() == nullptr) {
+ // TODO Better error msg.
+ RecordFailure(ERR(INTERNAL), "Unable to find class loader!");
+ return false;
+ }
+ holder->SetNewDexFileCookie(klass_index, AllocateDexFileCookie(dex_file_obj));
+ if (holder->GetNewDexFileCookie(klass_index) == nullptr) {
+ driver_->self_->AssertPendingOOMException();
+ driver_->self_->ClearException();
+ RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate dex file array for class loader");
+ return false;
+ }
+ holder->SetNewDexCache(klass_index, CreateNewDexCache(loader));
+ if (holder->GetNewDexCache(klass_index) == nullptr) {
+ driver_->self_->AssertPendingOOMException();
+ driver_->self_->ClearException();
+ RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate DexCache");
+ return false;
+ }
+
+ // We won't always need to set this field.
+ holder->SetOriginalDexFileBytes(klass_index, AllocateOrGetOriginalDexFileBytes());
+ if (holder->GetOriginalDexFileBytes(klass_index) == nullptr) {
+ driver_->self_->AssertPendingOOMException();
+ driver_->self_->ClearException();
+ RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate array for original dex file");
+ return false;
+ }
+ return true;
+}
+
bool Redefiner::CheckAllRedefinitionAreValid() {
for (Redefiner::ClassRedefinition& redef : redefinitions_) {
if (!redef.CheckRedefinitionIsValid()) {
@@ -802,33 +919,11 @@
bool Redefiner::FinishAllRemainingAllocations(RedefinitionDataHolder& holder) {
int32_t cnt = 0;
- art::StackHandleScope<4> hs(self_);
- art::MutableHandle<art::mirror::Object> java_dex_file(hs.NewHandle<art::mirror::Object>(nullptr));
- art::MutableHandle<art::mirror::ClassLoader> source_class_loader(
- hs.NewHandle<art::mirror::ClassLoader>(nullptr));
- art::MutableHandle<art::mirror::LongArray> new_dex_file_cookie(
- hs.NewHandle<art::mirror::LongArray>(nullptr));
- art::MutableHandle<art::mirror::DexCache> new_dex_cache(
- hs.NewHandle<art::mirror::DexCache>(nullptr));
for (Redefiner::ClassRedefinition& redef : redefinitions_) {
- // Reset the out pointers to null
- source_class_loader.Assign(nullptr);
- java_dex_file.Assign(nullptr);
- new_dex_file_cookie.Assign(nullptr);
- new_dex_cache.Assign(nullptr);
// Allocate the data this redefinition requires.
- if (!redef.FinishRemainingAllocations(&source_class_loader,
- &java_dex_file,
- &new_dex_file_cookie,
- &new_dex_cache)) {
+ if (!redef.FinishRemainingAllocations(cnt, &holder)) {
return false;
}
- // Save the allocated data into the holder.
- holder.SetSourceClassLoader(cnt, source_class_loader.Get());
- holder.SetJavaDexFile(cnt, java_dex_file.Get());
- holder.SetNewDexFileCookie(cnt, new_dex_file_cookie.Get());
- holder.SetNewDexCache(cnt, new_dex_cache.Get());
- holder.SetMirrorClass(cnt, redef.GetMirrorClass());
cnt++;
}
return true;
@@ -894,7 +989,7 @@
redef.UpdateJavaDexFile(holder.GetJavaDexFile(cnt), holder.GetNewDexFileCookie(cnt));
// TODO Rewrite so we don't do a stack walk for each and every class.
redef.FindAndAllocateObsoleteMethods(klass);
- redef.UpdateClass(klass, holder.GetNewDexCache(cnt));
+ redef.UpdateClass(klass, holder.GetNewDexCache(cnt), holder.GetOriginalDexFileBytes(cnt));
cnt++;
}
// Ensure that obsolete methods are deoptimized. This is needed since optimized methods may have
@@ -987,20 +1082,24 @@
}
// Performs updates to class that will allow us to verify it.
-void Redefiner::ClassRedefinition::UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
- art::ObjPtr<art::mirror::DexCache> new_dex_cache) {
- const art::DexFile::ClassDef* class_def = art::OatFile::OatDexFile::FindClassDef(
- *dex_file_, class_sig_.c_str(), art::ComputeModifiedUtf8Hash(class_sig_.c_str()));
- DCHECK(class_def != nullptr);
- UpdateMethods(mclass, new_dex_cache, *class_def);
+void Redefiner::ClassRedefinition::UpdateClass(
+ art::ObjPtr<art::mirror::Class> mclass,
+ art::ObjPtr<art::mirror::DexCache> new_dex_cache,
+ art::ObjPtr<art::mirror::ByteArray> original_dex_file) {
+ DCHECK_EQ(dex_file_->NumClassDefs(), 1u);
+ const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(0);
+ UpdateMethods(mclass, new_dex_cache, class_def);
UpdateFields(mclass);
// Update the class fields.
// Need to update class last since the ArtMethod gets its DexFile from the class (which is needed
// to call GetReturnTypeDescriptor and GetParameterTypeList above).
mclass->SetDexCache(new_dex_cache.Ptr());
- mclass->SetDexClassDefIndex(dex_file_->GetIndexForClassDef(*class_def));
+ mclass->SetDexClassDefIndex(dex_file_->GetIndexForClassDef(class_def));
mclass->SetDexTypeIndex(dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(class_sig_.c_str())));
+ art::ObjPtr<art::mirror::ClassExt> ext(mclass->GetExtData());
+ CHECK(!ext.IsNull());
+ ext->SetOriginalDexFileBytes(original_dex_file);
}
void Redefiner::ClassRedefinition::UpdateJavaDexFile(
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index 8626bc5..29a7e1f 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -38,6 +38,7 @@
#include "art_jvmti.h"
#include "art_method.h"
+#include "base/array_slice.h"
#include "class_linker.h"
#include "dex_file.h"
#include "gc_root-inl.h"
@@ -56,6 +57,7 @@
#include "obj_ptr.h"
#include "scoped_thread_state_change-inl.h"
#include "stack.h"
+#include "ti_class_definition.h"
#include "thread_list.h"
#include "transform.h"
#include "utf.h"
@@ -72,13 +74,25 @@
public:
// Redefine the given classes with the given dex data. Note this function does not take ownership
// of the dex_data pointers. It is not used after this call however and may be freed if desired.
+ // The caller is responsible for freeing it. The runtime makes its own copy of the data. This
+ // function does not call the transformation events.
+ // TODO Check modified flag of the definitions.
+ static jvmtiError RedefineClassesDirect(ArtJvmTiEnv* env,
+ art::Runtime* runtime,
+ art::Thread* self,
+ const std::vector<ArtClassDefinition>& definitions,
+ /*out*/std::string* error_msg);
+
+ // Redefine the given classes with the given dex data. Note this function does not take ownership
+ // of the dex_data pointers. It is not used after this call however and may be freed if desired.
// The caller is responsible for freeing it. The runtime makes its own copy of the data.
+ // TODO This function should call the transformation events.
static jvmtiError RedefineClasses(ArtJvmTiEnv* env,
art::Runtime* runtime,
art::Thread* self,
jint class_count,
const jvmtiClassDefinition* definitions,
- std::string* error_msg);
+ /*out*/std::string* error_msg);
static jvmtiError IsModifiableClass(jvmtiEnv* env, jclass klass, jboolean* is_redefinable);
@@ -88,7 +102,8 @@
ClassRedefinition(Redefiner* driver,
jclass klass,
const art::DexFile* redefined_dex_file,
- const char* class_sig)
+ const char* class_sig,
+ art::ArraySlice<const unsigned char> orig_dex_file)
REQUIRES_SHARED(art::Locks::mutator_lock_);
// NO_THREAD_SAFETY_ANALYSIS so we can unlock the class in the destructor.
@@ -99,7 +114,8 @@
: driver_(other.driver_),
klass_(other.klass_),
dex_file_(std::move(other.dex_file_)),
- class_sig_(std::move(other.class_sig_)) {
+ class_sig_(std::move(other.class_sig_)),
+ original_dex_file_(other.original_dex_file_) {
other.driver_ = nullptr;
}
@@ -118,15 +134,15 @@
art::mirror::LongArray* AllocateDexFileCookie(art::Handle<art::mirror::Object> j_dex_file_obj)
REQUIRES_SHARED(art::Locks::mutator_lock_);
+ // This may return nullptr with a OOME pending if allocation fails.
+ art::mirror::ByteArray* AllocateOrGetOriginalDexFileBytes()
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+
void RecordFailure(jvmtiError e, const std::string& err) {
driver_->RecordFailure(e, class_sig_, err);
}
- bool FinishRemainingAllocations(
- /*out*/art::MutableHandle<art::mirror::ClassLoader>* source_class_loader,
- /*out*/art::MutableHandle<art::mirror::Object>* source_dex_file_obj,
- /*out*/art::MutableHandle<art::mirror::LongArray>* new_dex_file_cookie,
- /*out*/art::MutableHandle<art::mirror::DexCache>* new_dex_cache)
+ bool FinishRemainingAllocations(int32_t klass_index, /*out*/RedefinitionDataHolder* holder)
REQUIRES_SHARED(art::Locks::mutator_lock_);
void FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass)
@@ -179,7 +195,8 @@
REQUIRES(art::Locks::mutator_lock_);
void UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
- art::ObjPtr<art::mirror::DexCache> new_dex_cache)
+ art::ObjPtr<art::mirror::DexCache> new_dex_cache,
+ art::ObjPtr<art::mirror::ByteArray> original_dex_file)
REQUIRES(art::Locks::mutator_lock_);
void ReleaseDexFile() REQUIRES_SHARED(art::Locks::mutator_lock_);
@@ -189,6 +206,7 @@
jclass klass_;
std::unique_ptr<const art::DexFile> dex_file_;
std::string class_sig_;
+ art::ArraySlice<const unsigned char> original_dex_file_;
};
jvmtiError result_;
@@ -209,7 +227,7 @@
redefinitions_(),
error_msg_(error_msg) { }
- jvmtiError AddRedefinition(ArtJvmTiEnv* env, const jvmtiClassDefinition& def)
+ jvmtiError AddRedefinition(ArtJvmTiEnv* env, const ArtClassDefinition& def)
REQUIRES_SHARED(art::Locks::mutator_lock_);
static jvmtiError GetClassRedefinitionError(art::Handle<art::mirror::Class> klass,
diff --git a/runtime/openjdkjvmti/ti_thread.cc b/runtime/openjdkjvmti/ti_thread.cc
index 2bcdd8c..9f81d6b 100644
--- a/runtime/openjdkjvmti/ti_thread.cc
+++ b/runtime/openjdkjvmti/ti_thread.cc
@@ -31,10 +31,12 @@
#include "ti_thread.h"
+#include "android-base/strings.h"
#include "art_field.h"
#include "art_jvmti.h"
#include "base/logging.h"
#include "base/mutex.h"
+#include "events-inl.h"
#include "gc/system_weak.h"
#include "gc_root-inl.h"
#include "jni_internal.h"
@@ -43,6 +45,8 @@
#include "mirror/string.h"
#include "obj_ptr.h"
#include "runtime.h"
+#include "runtime_callbacks.h"
+#include "ScopedLocalRef.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-inl.h"
#include "thread_list.h"
@@ -50,6 +54,77 @@
namespace openjdkjvmti {
+struct ThreadCallback : public art::ThreadLifecycleCallback, public art::RuntimePhaseCallback {
+ jthread GetThreadObject(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (self->GetPeer() == nullptr) {
+ return nullptr;
+ }
+ return self->GetJniEnv()->AddLocalReference<jthread>(self->GetPeer());
+ }
+ void Post(art::Thread* self, ArtJvmtiEvent type) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ DCHECK_EQ(self, art::Thread::Current());
+ ScopedLocalRef<jthread> thread(self->GetJniEnv(), GetThreadObject(self));
+ art::ScopedThreadSuspension sts(self, art::ThreadState::kNative);
+ event_handler->DispatchEvent(self, type, self->GetJniEnv(), thread.get());
+ }
+
+ void ThreadStart(art::Thread* self) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (!started) {
+ // Runtime isn't started. We only expect at most the signal handler or JIT threads to be
+ // started here.
+ if (art::kIsDebugBuild) {
+ std::string name;
+ self->GetThreadName(name);
+ if (name != "Signal Catcher" && !android::base::StartsWith(name, "Jit thread pool")) {
+ LOG(FATAL) << "Unexpected thread before start: " << name;
+ }
+ }
+ return;
+ }
+ Post(self, ArtJvmtiEvent::kThreadStart);
+ }
+
+ void ThreadDeath(art::Thread* self) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ Post(self, ArtJvmtiEvent::kThreadEnd);
+ }
+
+ void NextRuntimePhase(RuntimePhase phase) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (phase == RuntimePhase::kInit) {
+ // We moved to VMInit. Report the main thread as started (it was attached early, and must
+ // not be reported until Init.
+ started = true;
+ Post(art::Thread::Current(), ArtJvmtiEvent::kThreadStart);
+ }
+ }
+
+ EventHandler* event_handler = nullptr;
+ bool started = false;
+};
+
+ThreadCallback gThreadCallback;
+
+void ThreadUtil::Register(EventHandler* handler) {
+ art::Runtime* runtime = art::Runtime::Current();
+
+ gThreadCallback.started = runtime->IsStarted();
+ gThreadCallback.event_handler = handler;
+
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Add thread callback");
+ runtime->GetRuntimeCallbacks()->AddThreadLifecycleCallback(&gThreadCallback);
+ runtime->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&gThreadCallback);
+}
+
+void ThreadUtil::Unregister() {
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Remove thread callback");
+ art::Runtime* runtime = art::Runtime::Current();
+ runtime->GetRuntimeCallbacks()->RemoveThreadLifecycleCallback(&gThreadCallback);
+ runtime->GetRuntimeCallbacks()->RemoveRuntimePhaseCallback(&gThreadCallback);
+}
+
jvmtiError ThreadUtil::GetCurrentThread(jvmtiEnv* env ATTRIBUTE_UNUSED, jthread* thread_ptr) {
art::Thread* self = art::Thread::Current();
@@ -443,4 +518,80 @@
return ERR(NONE);
}
+struct AgentData {
+ const void* arg;
+ jvmtiStartFunction proc;
+ jthread thread;
+ JavaVM* java_vm;
+ jvmtiEnv* jvmti_env;
+ jint priority;
+};
+
+static void* AgentCallback(void* arg) {
+ std::unique_ptr<AgentData> data(reinterpret_cast<AgentData*>(arg));
+ CHECK(data->thread != nullptr);
+
+ // We already have a peer. So call our special Attach function.
+ art::Thread* self = art::Thread::Attach("JVMTI Agent thread", true, data->thread);
+ CHECK(self != nullptr);
+ // The name in Attach() is only for logging. Set the thread name. This is important so
+ // that the thread is no longer seen as starting up.
+ {
+ art::ScopedObjectAccess soa(self);
+ self->SetThreadName("JVMTI Agent thread");
+ }
+
+ // Release the peer.
+ JNIEnv* env = self->GetJniEnv();
+ env->DeleteGlobalRef(data->thread);
+ data->thread = nullptr;
+
+ // Run the agent code.
+ data->proc(data->jvmti_env, env, const_cast<void*>(data->arg));
+
+ // Detach the thread.
+ int detach_result = data->java_vm->DetachCurrentThread();
+ CHECK_EQ(detach_result, 0);
+
+ return nullptr;
+}
+
+jvmtiError ThreadUtil::RunAgentThread(jvmtiEnv* jvmti_env,
+ jthread thread,
+ jvmtiStartFunction proc,
+ const void* arg,
+ jint priority) {
+ if (priority < JVMTI_THREAD_MIN_PRIORITY || priority > JVMTI_THREAD_MAX_PRIORITY) {
+ return ERR(INVALID_PRIORITY);
+ }
+ JNIEnv* env = art::Thread::Current()->GetJniEnv();
+ if (thread == nullptr || !env->IsInstanceOf(thread, art::WellKnownClasses::java_lang_Thread)) {
+ return ERR(INVALID_THREAD);
+ }
+ if (proc == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ std::unique_ptr<AgentData> data(new AgentData);
+ data->arg = arg;
+ data->proc = proc;
+ // We need a global ref for Java objects, as local refs will be invalid.
+ data->thread = env->NewGlobalRef(thread);
+ data->java_vm = art::Runtime::Current()->GetJavaVM();
+ data->jvmti_env = jvmti_env;
+ data->priority = priority;
+
+ pthread_t pthread;
+ int pthread_create_result = pthread_create(&pthread,
+ nullptr,
+ &AgentCallback,
+ reinterpret_cast<void*>(data.get()));
+ if (pthread_create_result != 0) {
+ return ERR(INTERNAL);
+ }
+ data.release();
+
+ return ERR(NONE);
+}
+
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_thread.h b/runtime/openjdkjvmti/ti_thread.h
index 290e9d4..f6f93ee 100644
--- a/runtime/openjdkjvmti/ti_thread.h
+++ b/runtime/openjdkjvmti/ti_thread.h
@@ -37,8 +37,13 @@
namespace openjdkjvmti {
+class EventHandler;
+
class ThreadUtil {
public:
+ static void Register(EventHandler* event_handler);
+ static void Unregister();
+
static jvmtiError GetAllThreads(jvmtiEnv* env, jint* threads_count_ptr, jthread** threads_ptr);
static jvmtiError GetCurrentThread(jvmtiEnv* env, jthread* thread_ptr);
@@ -49,6 +54,12 @@
static jvmtiError SetThreadLocalStorage(jvmtiEnv* env, jthread thread, const void* data);
static jvmtiError GetThreadLocalStorage(jvmtiEnv* env, jthread thread, void** data_ptr);
+
+ static jvmtiError RunAgentThread(jvmtiEnv* env,
+ jthread thread,
+ jvmtiStartFunction proc,
+ const void* arg,
+ jint priority);
};
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/transform.cc b/runtime/openjdkjvmti/transform.cc
index f545125..af4fb71 100644
--- a/runtime/openjdkjvmti/transform.cc
+++ b/runtime/openjdkjvmti/transform.cc
@@ -38,6 +38,7 @@
#include "class_linker.h"
#include "dex_file.h"
#include "dex_file_types.h"
+#include "events-inl.h"
#include "gc_root-inl.h"
#include "globals.h"
#include "jni_env_ext-inl.h"
@@ -46,18 +47,83 @@
#include "mem_map.h"
#include "mirror/array.h"
#include "mirror/class-inl.h"
+#include "mirror/class_ext.h"
#include "mirror/class_loader-inl.h"
#include "mirror/string-inl.h"
#include "oat_file.h"
#include "scoped_thread_state_change-inl.h"
#include "stack.h"
#include "thread_list.h"
+#include "ti_redefine.h"
#include "transform.h"
#include "utf.h"
#include "utils/dex_cache_arrays_layout-inl.h"
namespace openjdkjvmti {
+jvmtiError Transformer::RetransformClassesDirect(
+ ArtJvmTiEnv* env,
+ art::Thread* self,
+ /*in-out*/std::vector<ArtClassDefinition>* definitions) {
+ for (ArtClassDefinition& def : *definitions) {
+ jint new_len = -1;
+ unsigned char* new_data = nullptr;
+ // Static casts are so that we get the right template initialization for the special event
+ // handling code required by the ClassFileLoadHooks.
+ gEventHandler.DispatchEvent(self,
+ ArtJvmtiEvent::kClassFileLoadHookRetransformable,
+ GetJniEnv(env),
+ static_cast<jclass>(def.klass),
+ static_cast<jobject>(def.loader),
+ static_cast<const char*>(def.name.c_str()),
+ static_cast<jobject>(def.protection_domain),
+ static_cast<jint>(def.dex_len),
+ static_cast<const unsigned char*>(def.dex_data.get()),
+ static_cast<jint*>(&new_len),
+ static_cast<unsigned char**>(&new_data));
+ def.SetNewDexData(env, new_len, new_data);
+ }
+ return OK;
+}
+
+jvmtiError Transformer::RetransformClasses(ArtJvmTiEnv* env,
+ art::Runtime* runtime,
+ art::Thread* self,
+ jint class_count,
+ const jclass* classes,
+ /*out*/std::string* error_msg) {
+ if (env == nullptr) {
+ *error_msg = "env was null!";
+ return ERR(INVALID_ENVIRONMENT);
+ } else if (class_count < 0) {
+ *error_msg = "class_count was less then 0";
+ return ERR(ILLEGAL_ARGUMENT);
+ } else if (class_count == 0) {
+ // We don't actually need to do anything. Just return OK.
+ return OK;
+ } else if (classes == nullptr) {
+ *error_msg = "null classes!";
+ return ERR(NULL_POINTER);
+ }
+ // A holder that will Deallocate all the class bytes buffers on destruction.
+ std::vector<ArtClassDefinition> definitions;
+ jvmtiError res = OK;
+ for (jint i = 0; i < class_count; i++) {
+ ArtClassDefinition def;
+ res = FillInTransformationData(env, classes[i], &def);
+ if (res != OK) {
+ return res;
+ }
+ definitions.push_back(std::move(def));
+ }
+ res = RetransformClassesDirect(env, self, &definitions);
+ if (res != OK) {
+ return res;
+ }
+ return Redefiner::RedefineClassesDirect(env, runtime, self, definitions, error_msg);
+}
+
+// TODO Move this somewhere else, ti_class?
jvmtiError GetClassLocation(ArtJvmTiEnv* env, jclass klass, /*out*/std::string* location) {
JNIEnv* jni_env = nullptr;
jint ret = env->art_vm->GetEnv(reinterpret_cast<void**>(&jni_env), JNI_VERSION_1_1);
@@ -73,42 +139,74 @@
return OK;
}
+static jvmtiError CopyDataIntoJvmtiBuffer(ArtJvmTiEnv* env,
+ const unsigned char* source,
+ jint len,
+ /*out*/unsigned char** dest) {
+ jvmtiError res = env->Allocate(len, dest);
+ if (res != OK) {
+ return res;
+ }
+ memcpy(reinterpret_cast<void*>(*dest),
+ reinterpret_cast<const void*>(source),
+ len);
+ return OK;
+}
+
+jvmtiError Transformer::GetDexDataForRetransformation(ArtJvmTiEnv* env,
+ art::Handle<art::mirror::Class> klass,
+ /*out*/jint* dex_data_len,
+ /*out*/unsigned char** dex_data) {
+ art::StackHandleScope<2> hs(art::Thread::Current());
+ art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->GetExtData()));
+ if (!ext.IsNull()) {
+ art::Handle<art::mirror::ByteArray> orig_dex(hs.NewHandle(ext->GetOriginalDexFileBytes()));
+ if (!orig_dex.IsNull()) {
+ *dex_data_len = static_cast<jint>(orig_dex->GetLength());
+ return CopyDataIntoJvmtiBuffer(env,
+ reinterpret_cast<const unsigned char*>(orig_dex->GetData()),
+ *dex_data_len,
+ /*out*/dex_data);
+ }
+ }
+ // TODO De-quicken the dex file before passing it to the agents.
+ LOG(WARNING) << "Dex file is not de-quickened yet! Quickened dex instructions might be present";
+ const art::DexFile& dex = klass->GetDexFile();
+ *dex_data_len = static_cast<jint>(dex.Size());
+ return CopyDataIntoJvmtiBuffer(env, dex.Begin(), *dex_data_len, /*out*/dex_data);
+}
+
// TODO Move this function somewhere more appropriate.
// Gets the data surrounding the given class.
-jvmtiError GetTransformationData(ArtJvmTiEnv* env,
- jclass klass,
- /*out*/std::string* location,
- /*out*/JNIEnv** jni_env_ptr,
- /*out*/jobject* loader,
- /*out*/std::string* name,
- /*out*/jobject* protection_domain,
- /*out*/jint* data_len,
- /*out*/unsigned char** dex_data) {
- jint ret = env->art_vm->GetEnv(reinterpret_cast<void**>(jni_env_ptr), JNI_VERSION_1_1);
- if (ret != JNI_OK) {
+// TODO Make this less magical.
+jvmtiError Transformer::FillInTransformationData(ArtJvmTiEnv* env,
+ jclass klass,
+ ArtClassDefinition* def) {
+ JNIEnv* jni_env = GetJniEnv(env);
+ if (jni_env == nullptr) {
// TODO Different error might be better?
return ERR(INTERNAL);
}
- JNIEnv* jni_env = *jni_env_ptr;
art::ScopedObjectAccess soa(jni_env);
art::StackHandleScope<3> hs(art::Thread::Current());
art::Handle<art::mirror::Class> hs_klass(hs.NewHandle(soa.Decode<art::mirror::Class>(klass)));
- *loader = soa.AddLocalReference<jobject>(hs_klass->GetClassLoader());
- *name = art::mirror::Class::ComputeName(hs_klass)->ToModifiedUtf8();
- // TODO is this always null?
- *protection_domain = nullptr;
- const art::DexFile& dex = hs_klass->GetDexFile();
- *location = dex.GetLocation();
- *data_len = static_cast<jint>(dex.Size());
- // TODO We should maybe change env->Allocate to allow us to mprotect this memory and stop writes.
- jvmtiError alloc_error = env->Allocate(*data_len, dex_data);
- if (alloc_error != OK) {
- return alloc_error;
+ if (hs_klass.IsNull()) {
+ return ERR(INVALID_CLASS);
}
- // Copy the data into a temporary buffer.
- memcpy(reinterpret_cast<void*>(*dex_data),
- reinterpret_cast<const void*>(dex.Begin()),
- *data_len);
+ def->klass = klass;
+ def->loader = soa.AddLocalReference<jobject>(hs_klass->GetClassLoader());
+ def->name = art::mirror::Class::ComputeName(hs_klass)->ToModifiedUtf8();
+ // TODO is this always null?
+ def->protection_domain = nullptr;
+ if (def->dex_data.get() == nullptr) {
+ unsigned char* new_data;
+ jvmtiError res = GetDexDataForRetransformation(env, hs_klass, &def->dex_len, &new_data);
+ if (res == OK) {
+ def->dex_data = MakeJvmtiUniquePtr(env, new_data);
+ } else {
+ return res;
+ }
+ }
return OK;
}
diff --git a/runtime/openjdkjvmti/transform.h b/runtime/openjdkjvmti/transform.h
index 0ad5099..65f2ae1 100644
--- a/runtime/openjdkjvmti/transform.h
+++ b/runtime/openjdkjvmti/transform.h
@@ -37,22 +37,37 @@
#include <jni.h>
#include "art_jvmti.h"
+#include "ti_class_definition.h"
#include "jvmti.h"
namespace openjdkjvmti {
jvmtiError GetClassLocation(ArtJvmTiEnv* env, jclass klass, /*out*/std::string* location);
-// Gets the data surrounding the given class.
-jvmtiError GetTransformationData(ArtJvmTiEnv* env,
- jclass klass,
- /*out*/std::string* location,
- /*out*/JNIEnv** jni_env_ptr,
- /*out*/jobject* loader,
- /*out*/std::string* name,
- /*out*/jobject* protection_domain,
- /*out*/jint* data_len,
- /*out*/unsigned char** dex_data);
+class Transformer {
+ public:
+ static jvmtiError RetransformClassesDirect(
+ ArtJvmTiEnv* env, art::Thread* self, /*in-out*/std::vector<ArtClassDefinition>* definitions);
+
+ static jvmtiError RetransformClasses(ArtJvmTiEnv* env,
+ art::Runtime* runtime,
+ art::Thread* self,
+ jint class_count,
+ const jclass* classes,
+ /*out*/std::string* error_msg);
+
+ // Gets the data surrounding the given class.
+ static jvmtiError FillInTransformationData(ArtJvmTiEnv* env,
+ jclass klass,
+ ArtClassDefinition* def);
+
+ private:
+ static jvmtiError GetDexDataForRetransformation(ArtJvmTiEnv* env,
+ art::Handle<art::mirror::Class> klass,
+ /*out*/jint* dex_data_length,
+ /*out*/unsigned char** dex_data)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+};
} // namespace openjdkjvmti
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index a72159b..d1ad77c 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -302,6 +302,9 @@
.IntoKey(M::Plugins)
.Define("-Xfully-deoptable")
.IntoKey(M::FullyDeoptable)
+ .Define("-XX:ThreadSuspendTimeout=_") // in ms
+ .WithType<MillisecondsToNanoseconds>() // store as ns
+ .IntoKey(M::ThreadSuspendTimeout)
.Ignore({
"-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
"-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
@@ -724,6 +727,7 @@
UsageMessage(stream, " -XX:MaxSpinsBeforeThinLockInflation=integervalue\n");
UsageMessage(stream, " -XX:LongPauseLogThreshold=integervalue\n");
UsageMessage(stream, " -XX:LongGCLogThreshold=integervalue\n");
+ UsageMessage(stream, " -XX:ThreadSuspendTimeout=integervalue\n");
UsageMessage(stream, " -XX:DumpGCPerformanceOnShutdown\n");
UsageMessage(stream, " -XX:DumpJITInfoOnShutdown\n");
UsageMessage(stream, " -XX:IgnoreMaxFootprint\n");
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 75176f9..a2b4cb3 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -216,43 +216,54 @@
}
bool BuildArgArrayFromObjectArray(ObjPtr<mirror::Object> receiver,
- ObjPtr<mirror::ObjectArray<mirror::Object>> args,
- ArtMethod* m)
+ ObjPtr<mirror::ObjectArray<mirror::Object>> raw_args,
+ ArtMethod* m,
+ Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
const DexFile::TypeList* classes = m->GetParameterTypeList();
// Set receiver if non-null (method is not static)
if (receiver != nullptr) {
Append(receiver);
}
+ StackHandleScope<2> hs(self);
+ MutableHandle<mirror::Object> arg(hs.NewHandle<mirror::Object>(nullptr));
+ Handle<mirror::ObjectArray<mirror::Object>> args(
+ hs.NewHandle<mirror::ObjectArray<mirror::Object>>(raw_args));
for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) {
- ObjPtr<mirror::Object> arg(args->Get(args_offset));
- if (((shorty_[i] == 'L') && (arg != nullptr)) || ((arg == nullptr && shorty_[i] != 'L'))) {
- // Note: The method's parameter's type must have been previously resolved.
+ arg.Assign(args->Get(args_offset));
+ if (((shorty_[i] == 'L') && (arg.Get() != nullptr)) ||
+ ((arg.Get() == nullptr && shorty_[i] != 'L'))) {
+ // TODO: The method's parameter's type must have been previously resolved, yet
+ // we've seen cases where it's not b/34440020.
ObjPtr<mirror::Class> dst_class(
m->GetClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_,
- false /* resolve */));
- DCHECK(dst_class != nullptr) << m->PrettyMethod() << " arg #" << i;
- if (UNLIKELY(arg == nullptr || !arg->InstanceOf(dst_class))) {
+ true /* resolve */));
+ if (dst_class.Ptr() == nullptr) {
+ CHECK(self->IsExceptionPending());
+ return false;
+ }
+ if (UNLIKELY(arg.Get() == nullptr || !arg->InstanceOf(dst_class))) {
ThrowIllegalArgumentException(
StringPrintf("method %s argument %zd has type %s, got %s",
m->PrettyMethod(false).c_str(),
args_offset + 1, // Humans don't count from 0.
mirror::Class::PrettyDescriptor(dst_class).c_str(),
- mirror::Object::PrettyTypeOf(arg).c_str()).c_str());
+ mirror::Object::PrettyTypeOf(arg.Get()).c_str()).c_str());
return false;
}
}
#define DO_FIRST_ARG(match_descriptor, get_fn, append) { \
- if (LIKELY(arg != nullptr && arg->GetClass()->DescriptorEquals(match_descriptor))) { \
+ if (LIKELY(arg.Get() != nullptr && \
+ arg->GetClass()->DescriptorEquals(match_descriptor))) { \
ArtField* primitive_field = arg->GetClass()->GetInstanceField(0); \
- append(primitive_field-> get_fn(arg));
+ append(primitive_field-> get_fn(arg.Get()));
#define DO_ARG(match_descriptor, get_fn, append) \
- } else if (LIKELY(arg != nullptr && \
+ } else if (LIKELY(arg.Get() != nullptr && \
arg->GetClass<>()->DescriptorEquals(match_descriptor))) { \
ArtField* primitive_field = arg->GetClass()->GetInstanceField(0); \
- append(primitive_field-> get_fn(arg));
+ append(primitive_field-> get_fn(arg.Get()));
#define DO_FAIL(expected) \
} else { \
@@ -266,14 +277,14 @@
ArtMethod::PrettyMethod(m, false).c_str(), \
args_offset + 1, \
expected, \
- mirror::Object::PrettyTypeOf(arg).c_str()).c_str()); \
+ mirror::Object::PrettyTypeOf(arg.Get()).c_str()).c_str()); \
} \
return false; \
} }
switch (shorty_[i]) {
case 'L':
- Append(arg);
+ Append(arg.Get());
break;
case 'Z':
DO_FIRST_ARG("Ljava/lang/Boolean;", GetBoolean, Append)
@@ -646,7 +657,7 @@
uint32_t shorty_len = 0;
const char* shorty = np_method->GetShorty(&shorty_len);
ArgArray arg_array(shorty, shorty_len);
- if (!arg_array.BuildArgArrayFromObjectArray(receiver, objects, np_method)) {
+ if (!arg_array.BuildArgArrayFromObjectArray(receiver, objects, np_method, soa.Self())) {
CHECK(soa.Self()->IsExceptionPending());
return nullptr;
}
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 55e1852..06cd7ff 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -137,6 +137,7 @@
#include "jit/profile_saver.h"
#include "quick/quick_method_frame_info.h"
#include "reflection.h"
+#include "runtime_callbacks.h"
#include "runtime_options.h"
#include "ScopedLocalRef.h"
#include "scoped_thread_state_change-inl.h"
@@ -253,10 +254,12 @@
pruned_dalvik_cache_(false),
// Initially assume we perceive jank in case the process state is never updated.
process_state_(kProcessStateJankPerceptible),
- zygote_no_threads_(false) {
+ zygote_no_threads_(false),
+ cha_(nullptr) {
CheckAsmSupportOffsetsAndSizes();
std::fill(callee_save_methods_, callee_save_methods_ + arraysize(callee_save_methods_), 0u);
interpreter::CheckInterpreterAsmConstants();
+ callbacks_.reset(new RuntimeCallbacks());
}
Runtime::~Runtime() {
@@ -301,6 +304,13 @@
Trace::Shutdown();
+ // Report death. Clients me require a working thread, still, so do it before GC completes and
+ // all non-daemon threads are done.
+ {
+ ScopedObjectAccess soa(self);
+ callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kDeath);
+ }
+
if (attach_shutdown_thread) {
DetachCurrentThread();
self = nullptr;
@@ -703,6 +713,13 @@
Thread::FinishStartup();
+ // Send the start phase event. We have to wait till here as this is when the main thread peer
+ // has just been generated, important root clinits have been run and JNI is completely functional.
+ {
+ ScopedObjectAccess soa(self);
+ callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kStart);
+ }
+
system_class_loader_ = CreateSystemClassLoader(this);
if (!is_zygote_) {
@@ -718,6 +735,13 @@
GetInstructionSetString(kRuntimeISA));
}
+ // Send the initialized phase event. Send it before starting daemons, as otherwise
+ // sending thread events becomes complicated.
+ {
+ ScopedObjectAccess soa(self);
+ callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kInit);
+ }
+
StartDaemonThreads();
{
@@ -1022,7 +1046,7 @@
monitor_list_ = new MonitorList;
monitor_pool_ = MonitorPool::Create();
- thread_list_ = new ThreadList;
+ thread_list_ = new ThreadList(runtime_options.GetOrDefault(Opt::ThreadSuspendTimeout));
intern_table_ = new InternTable;
verify_ = runtime_options.GetOrDefault(Opt::Verify);
@@ -1100,6 +1124,8 @@
if (runtime_options.Exists(Opt::JdwpOptions)) {
Dbg::ConfigureJdwp(runtime_options.GetOrDefault(Opt::JdwpOptions));
}
+ callbacks_->AddThreadLifecycleCallback(Dbg::GetThreadLifecycleCallback());
+ callbacks_->AddClassLoadCallback(Dbg::GetClassLoadCallback());
jit_options_.reset(jit::JitOptions::CreateFromRuntimeArguments(runtime_options));
if (IsAotCompiler()) {
@@ -1358,6 +1384,10 @@
LOG(ERROR) << "Unable to load an agent: " << err;
}
}
+ {
+ ScopedObjectAccess soa(self);
+ callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kInitialAgents);
+ }
VLOG(startup) << "Runtime::Init exiting";
@@ -1370,7 +1400,7 @@
constexpr const char* plugin_name = kIsDebugBuild ? "libopenjdkjvmtid.so" : "libopenjdkjvmti.so";
// Is the plugin already loaded?
- for (Plugin p : *plugins) {
+ for (const Plugin& p : *plugins) {
if (p.GetLibrary() == plugin_name) {
return true;
}
@@ -1547,6 +1577,12 @@
thread_list_->DumpForSigQuit(os);
BaseMutex::DumpAll(os);
+
+ // Inform anyone else who is interested in SigQuit.
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ callbacks_->SigQuit();
+ }
}
void Runtime::DumpLockHolders(std::ostream& os) {
@@ -2253,4 +2289,8 @@
Runtime::Abort(abort_message);
}
+RuntimeCallbacks* Runtime::GetRuntimeCallbacks() {
+ return callbacks_.get();
+}
+
} // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index cf23d05..f7d6810 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -28,6 +28,7 @@
#include "arch/instruction_set.h"
#include "base/macros.h"
+#include "base/mutex.h"
#include "dex_file_types.h"
#include "experimental_flags.h"
#include "gc_root.h"
@@ -89,6 +90,7 @@
class OatFileManager;
class Plugin;
struct RuntimeArgumentMap;
+class RuntimeCallbacks;
class SignalCatcher;
class StackOverflowHandler;
class SuspensionHandler;
@@ -659,6 +661,8 @@
void AttachAgent(const std::string& agent_arg);
+ RuntimeCallbacks* GetRuntimeCallbacks();
+
private:
static void InitPlatformSignalHandlers();
@@ -916,6 +920,8 @@
ClassHierarchyAnalysis* cha_;
+ std::unique_ptr<RuntimeCallbacks> callbacks_;
+
DISALLOW_COPY_AND_ASSIGN(Runtime);
};
std::ostream& operator<<(std::ostream& os, const Runtime::CalleeSaveType& rhs);
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
new file mode 100644
index 0000000..25324b5
--- /dev/null
+++ b/runtime/runtime_callbacks.cc
@@ -0,0 +1,134 @@
+/*
+ * 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 "runtime_callbacks.h"
+
+#include <algorithm>
+
+#include "base/macros.h"
+#include "class_linker.h"
+#include "thread.h"
+
+namespace art {
+
+void RuntimeCallbacks::AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) {
+ thread_callbacks_.push_back(cb);
+}
+
+template <typename T>
+ALWAYS_INLINE
+static inline void Remove(T* cb, std::vector<T*>* data) {
+ auto it = std::find(data->begin(), data->end(), cb);
+ if (it != data->end()) {
+ data->erase(it);
+ }
+}
+
+void RuntimeCallbacks::RemoveThreadLifecycleCallback(ThreadLifecycleCallback* cb) {
+ Remove(cb, &thread_callbacks_);
+}
+
+void RuntimeCallbacks::ThreadStart(Thread* self) {
+ for (ThreadLifecycleCallback* cb : thread_callbacks_) {
+ cb->ThreadStart(self);
+ }
+}
+
+void RuntimeCallbacks::ThreadDeath(Thread* self) {
+ for (ThreadLifecycleCallback* cb : thread_callbacks_) {
+ cb->ThreadDeath(self);
+ }
+}
+
+void RuntimeCallbacks::AddClassLoadCallback(ClassLoadCallback* cb) {
+ class_callbacks_.push_back(cb);
+}
+
+void RuntimeCallbacks::RemoveClassLoadCallback(ClassLoadCallback* cb) {
+ Remove(cb, &class_callbacks_);
+}
+
+void RuntimeCallbacks::ClassLoad(Handle<mirror::Class> klass) {
+ for (ClassLoadCallback* cb : class_callbacks_) {
+ cb->ClassLoad(klass);
+ }
+}
+
+void RuntimeCallbacks::ClassPreDefine(const char* descriptor,
+ Handle<mirror::Class> temp_class,
+ Handle<mirror::ClassLoader> loader,
+ const DexFile& initial_dex_file,
+ const DexFile::ClassDef& initial_class_def,
+ /*out*/DexFile const** final_dex_file,
+ /*out*/DexFile::ClassDef const** final_class_def) {
+ DexFile const* current_dex_file = &initial_dex_file;
+ DexFile::ClassDef const* current_class_def = &initial_class_def;
+ for (ClassLoadCallback* cb : class_callbacks_) {
+ DexFile const* new_dex_file = nullptr;
+ DexFile::ClassDef const* new_class_def = nullptr;
+ cb->ClassPreDefine(descriptor,
+ temp_class,
+ loader,
+ *current_dex_file,
+ *current_class_def,
+ &new_dex_file,
+ &new_class_def);
+ if ((new_dex_file != nullptr && new_dex_file != current_dex_file) ||
+ (new_class_def != nullptr && new_class_def != current_class_def)) {
+ DCHECK(new_dex_file != nullptr && new_class_def != nullptr);
+ current_dex_file = new_dex_file;
+ current_class_def = new_class_def;
+ }
+ }
+ *final_dex_file = current_dex_file;
+ *final_class_def = current_class_def;
+}
+
+void RuntimeCallbacks::ClassPrepare(Handle<mirror::Class> temp_klass, Handle<mirror::Class> klass) {
+ for (ClassLoadCallback* cb : class_callbacks_) {
+ cb->ClassPrepare(temp_klass, klass);
+ }
+}
+
+void RuntimeCallbacks::AddRuntimeSigQuitCallback(RuntimeSigQuitCallback* cb) {
+ sigquit_callbacks_.push_back(cb);
+}
+
+void RuntimeCallbacks::RemoveRuntimeSigQuitCallback(RuntimeSigQuitCallback* cb) {
+ Remove(cb, &sigquit_callbacks_);
+}
+
+void RuntimeCallbacks::SigQuit() {
+ for (RuntimeSigQuitCallback* cb : sigquit_callbacks_) {
+ cb->SigQuit();
+ }
+}
+
+void RuntimeCallbacks::AddRuntimePhaseCallback(RuntimePhaseCallback* cb) {
+ phase_callbacks_.push_back(cb);
+}
+
+void RuntimeCallbacks::RemoveRuntimePhaseCallback(RuntimePhaseCallback* cb) {
+ Remove(cb, &phase_callbacks_);
+}
+
+void RuntimeCallbacks::NextRuntimePhase(RuntimePhaseCallback::RuntimePhase phase) {
+ for (RuntimePhaseCallback* cb : phase_callbacks_) {
+ cb->NextRuntimePhase(phase);
+ }
+}
+
+} // namespace art
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
new file mode 100644
index 0000000..d321254
--- /dev/null
+++ b/runtime/runtime_callbacks.h
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_RUNTIME_CALLBACKS_H_
+#define ART_RUNTIME_RUNTIME_CALLBACKS_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "dex_file.h"
+#include "handle.h"
+
+namespace art {
+
+namespace mirror {
+class Class;
+class ClassLoader;
+} // namespace mirror
+
+class ClassLoadCallback;
+class Thread;
+class ThreadLifecycleCallback;
+
+// Note: RuntimeCallbacks uses the mutator lock to synchronize the callback lists. A thread must
+// hold the exclusive lock to add or remove a listener. A thread must hold the shared lock
+// to dispatch an event. This setup is chosen as some clients may want to suspend the
+// dispatching thread or all threads.
+//
+// To make this safe, the following restrictions apply:
+// * Only the owner of a listener may ever add or remove said listener.
+// * A listener must never add or remove itself or any other listener while running.
+// * It is the responsibility of the owner to not remove the listener while it is running
+// (and suspended).
+//
+// The simplest way to satisfy these restrictions is to never remove a listener, and to do
+// any state checking (is the listener enabled) in the listener itself. For an example, see
+// Dbg.
+
+class RuntimeSigQuitCallback {
+ public:
+ virtual ~RuntimeSigQuitCallback() {}
+
+ virtual void SigQuit() REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+};
+
+class RuntimePhaseCallback {
+ public:
+ enum RuntimePhase {
+ kInitialAgents, // Initial agent loading is done.
+ kStart, // The runtime is started.
+ kInit, // The runtime is initialized (and will run user code soon).
+ kDeath, // The runtime just died.
+ };
+
+ virtual ~RuntimePhaseCallback() {}
+
+ virtual void NextRuntimePhase(RuntimePhase phase) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+};
+
+class RuntimeCallbacks {
+ public:
+ void AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) REQUIRES(Locks::mutator_lock_);
+ void RemoveThreadLifecycleCallback(ThreadLifecycleCallback* cb) REQUIRES(Locks::mutator_lock_);
+
+ void ThreadStart(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_);
+ void ThreadDeath(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void AddClassLoadCallback(ClassLoadCallback* cb) REQUIRES(Locks::mutator_lock_);
+ void RemoveClassLoadCallback(ClassLoadCallback* cb) REQUIRES(Locks::mutator_lock_);
+
+ void ClassLoad(Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
+ void ClassPrepare(Handle<mirror::Class> temp_klass, Handle<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void AddRuntimeSigQuitCallback(RuntimeSigQuitCallback* cb)
+ REQUIRES(Locks::mutator_lock_);
+ void RemoveRuntimeSigQuitCallback(RuntimeSigQuitCallback* cb)
+ REQUIRES(Locks::mutator_lock_);
+
+ void SigQuit() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void AddRuntimePhaseCallback(RuntimePhaseCallback* cb)
+ REQUIRES(Locks::mutator_lock_);
+ void RemoveRuntimePhaseCallback(RuntimePhaseCallback* cb)
+ REQUIRES(Locks::mutator_lock_);
+
+ void NextRuntimePhase(RuntimePhaseCallback::RuntimePhase phase)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void ClassPreDefine(const char* descriptor,
+ Handle<mirror::Class> temp_class,
+ Handle<mirror::ClassLoader> loader,
+ const DexFile& initial_dex_file,
+ const DexFile::ClassDef& initial_class_def,
+ /*out*/DexFile const** final_dex_file,
+ /*out*/DexFile::ClassDef const** final_class_def)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ std::vector<ThreadLifecycleCallback*> thread_callbacks_
+ GUARDED_BY(Locks::mutator_lock_);
+ std::vector<ClassLoadCallback*> class_callbacks_
+ GUARDED_BY(Locks::mutator_lock_);
+ std::vector<RuntimeSigQuitCallback*> sigquit_callbacks_
+ GUARDED_BY(Locks::mutator_lock_);
+ std::vector<RuntimePhaseCallback*> phase_callbacks_
+ GUARDED_BY(Locks::mutator_lock_);
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_RUNTIME_CALLBACKS_H_
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
new file mode 100644
index 0000000..66eb2ec
--- /dev/null
+++ b/runtime/runtime_callbacks_test.cc
@@ -0,0 +1,432 @@
+/*
+ * 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 "runtime_callbacks.h"
+
+#include "jni.h"
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <initializer_list>
+#include <memory>
+#include <string>
+
+#include "art_method-inl.h"
+#include "base/mutex.h"
+#include "class_linker.h"
+#include "common_runtime_test.h"
+#include "handle.h"
+#include "handle_scope-inl.h"
+#include "mem_map.h"
+#include "mirror/class-inl.h"
+#include "mirror/class_loader.h"
+#include "obj_ptr.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "ScopedLocalRef.h"
+#include "thread-inl.h"
+#include "thread_list.h"
+#include "well_known_classes.h"
+
+namespace art {
+
+class RuntimeCallbacksTest : public CommonRuntimeTest {
+ protected:
+ void SetUp() OVERRIDE {
+ CommonRuntimeTest::SetUp();
+
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ ScopedThreadSuspension sts(self, kWaitingForDebuggerToAttach);
+ ScopedSuspendAll ssa("RuntimeCallbacksTest SetUp");
+ AddListener();
+ }
+
+ void TearDown() OVERRIDE {
+ {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ ScopedThreadSuspension sts(self, kWaitingForDebuggerToAttach);
+ ScopedSuspendAll ssa("RuntimeCallbacksTest TearDown");
+ RemoveListener();
+ }
+
+ CommonRuntimeTest::TearDown();
+ }
+
+ virtual void AddListener() REQUIRES(Locks::mutator_lock_) = 0;
+ virtual void RemoveListener() REQUIRES(Locks::mutator_lock_) = 0;
+
+ void MakeExecutable(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
+ CHECK(klass != nullptr);
+ PointerSize pointer_size = class_linker_->GetImagePointerSize();
+ for (auto& m : klass->GetMethods(pointer_size)) {
+ if (!m.IsAbstract()) {
+ class_linker_->SetEntryPointsToInterpreter(&m);
+ }
+ }
+ }
+};
+
+class ThreadLifecycleCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest {
+ public:
+ static void* PthreadsCallback(void* arg ATTRIBUTE_UNUSED) {
+ // Attach.
+ Runtime* runtime = Runtime::Current();
+ CHECK(runtime->AttachCurrentThread("ThreadLifecycle test thread", true, nullptr, false));
+
+ // Detach.
+ runtime->DetachCurrentThread();
+
+ // Die...
+ return nullptr;
+ }
+
+ protected:
+ void AddListener() OVERRIDE REQUIRES(Locks::mutator_lock_) {
+ Runtime::Current()->GetRuntimeCallbacks()->AddThreadLifecycleCallback(&cb_);
+ }
+ void RemoveListener() OVERRIDE REQUIRES(Locks::mutator_lock_) {
+ Runtime::Current()->GetRuntimeCallbacks()->RemoveThreadLifecycleCallback(&cb_);
+ }
+
+ enum CallbackState {
+ kBase,
+ kStarted,
+ kDied,
+ kWrongStart,
+ kWrongDeath,
+ };
+
+ struct Callback : public ThreadLifecycleCallback {
+ void ThreadStart(Thread* self) OVERRIDE {
+ if (state == CallbackState::kBase) {
+ state = CallbackState::kStarted;
+ stored_self = self;
+ } else {
+ state = CallbackState::kWrongStart;
+ }
+ }
+
+ void ThreadDeath(Thread* self) OVERRIDE {
+ if (state == CallbackState::kStarted && self == stored_self) {
+ state = CallbackState::kDied;
+ } else {
+ state = CallbackState::kWrongDeath;
+ }
+ }
+
+ Thread* stored_self;
+ CallbackState state = CallbackState::kBase;
+ };
+
+ Callback cb_;
+};
+
+TEST_F(ThreadLifecycleCallbackRuntimeCallbacksTest, ThreadLifecycleCallbackJava) {
+ Thread* self = Thread::Current();
+
+ self->TransitionFromSuspendedToRunnable();
+ bool started = runtime_->Start();
+ ASSERT_TRUE(started);
+
+ cb_.state = CallbackState::kBase; // Ignore main thread attach.
+
+ {
+ ScopedObjectAccess soa(self);
+ MakeExecutable(soa.Decode<mirror::Class>(WellKnownClasses::java_lang_Thread));
+ }
+
+ JNIEnv* env = self->GetJniEnv();
+
+ ScopedLocalRef<jobject> thread_name(env,
+ env->NewStringUTF("ThreadLifecycleCallback test thread"));
+ ASSERT_TRUE(thread_name.get() != nullptr);
+
+ ScopedLocalRef<jobject> thread(env, env->AllocObject(WellKnownClasses::java_lang_Thread));
+ ASSERT_TRUE(thread.get() != nullptr);
+
+ env->CallNonvirtualVoidMethod(thread.get(),
+ WellKnownClasses::java_lang_Thread,
+ WellKnownClasses::java_lang_Thread_init,
+ runtime_->GetMainThreadGroup(),
+ thread_name.get(),
+ kMinThreadPriority,
+ JNI_FALSE);
+ ASSERT_FALSE(env->ExceptionCheck());
+
+ jmethodID start_id = env->GetMethodID(WellKnownClasses::java_lang_Thread, "start", "()V");
+ ASSERT_TRUE(start_id != nullptr);
+
+ env->CallVoidMethod(thread.get(), start_id);
+ ASSERT_FALSE(env->ExceptionCheck());
+
+ jmethodID join_id = env->GetMethodID(WellKnownClasses::java_lang_Thread, "join", "()V");
+ ASSERT_TRUE(join_id != nullptr);
+
+ env->CallVoidMethod(thread.get(), join_id);
+ ASSERT_FALSE(env->ExceptionCheck());
+
+ EXPECT_TRUE(cb_.state == CallbackState::kDied) << static_cast<int>(cb_.state);
+}
+
+TEST_F(ThreadLifecycleCallbackRuntimeCallbacksTest, ThreadLifecycleCallbackAttach) {
+ std::string error_msg;
+ std::unique_ptr<MemMap> stack(MemMap::MapAnonymous("ThreadLifecycleCallback Thread",
+ nullptr,
+ 128 * kPageSize, // Just some small stack.
+ PROT_READ | PROT_WRITE,
+ false,
+ false,
+ &error_msg));
+ ASSERT_FALSE(stack == nullptr) << error_msg;
+
+ const char* reason = "ThreadLifecycleCallback test thread";
+ pthread_attr_t attr;
+ CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), reason);
+ CHECK_PTHREAD_CALL(pthread_attr_setstack, (&attr, stack->Begin(), stack->Size()), reason);
+ pthread_t pthread;
+ CHECK_PTHREAD_CALL(pthread_create,
+ (&pthread,
+ &attr,
+ &ThreadLifecycleCallbackRuntimeCallbacksTest::PthreadsCallback,
+ this),
+ reason);
+ CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), reason);
+
+ CHECK_PTHREAD_CALL(pthread_join, (pthread, nullptr), "ThreadLifecycleCallback test shutdown");
+
+ // Detach is not a ThreadDeath event, so we expect to be in state Started.
+ EXPECT_TRUE(cb_.state == CallbackState::kStarted) << static_cast<int>(cb_.state);
+}
+
+class ClassLoadCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest {
+ protected:
+ void AddListener() OVERRIDE REQUIRES(Locks::mutator_lock_) {
+ Runtime::Current()->GetRuntimeCallbacks()->AddClassLoadCallback(&cb_);
+ }
+ void RemoveListener() OVERRIDE REQUIRES(Locks::mutator_lock_) {
+ Runtime::Current()->GetRuntimeCallbacks()->RemoveClassLoadCallback(&cb_);
+ }
+
+ bool Expect(std::initializer_list<const char*> list) {
+ if (cb_.data.size() != list.size()) {
+ PrintError(list);
+ return false;
+ }
+
+ if (!std::equal(cb_.data.begin(), cb_.data.end(), list.begin())) {
+ PrintError(list);
+ return false;
+ }
+
+ return true;
+ }
+
+ void PrintError(std::initializer_list<const char*> list) {
+ LOG(ERROR) << "Expected:";
+ for (const char* expected : list) {
+ LOG(ERROR) << " " << expected;
+ }
+ LOG(ERROR) << "Found:";
+ for (const auto& s : cb_.data) {
+ LOG(ERROR) << " " << s;
+ }
+ }
+
+ struct Callback : public ClassLoadCallback {
+ virtual void ClassPreDefine(const char* descriptor,
+ Handle<mirror::Class> klass ATTRIBUTE_UNUSED,
+ Handle<mirror::ClassLoader> class_loader ATTRIBUTE_UNUSED,
+ const DexFile& initial_dex_file,
+ const DexFile::ClassDef& initial_class_def ATTRIBUTE_UNUSED,
+ /*out*/DexFile const** final_dex_file ATTRIBUTE_UNUSED,
+ /*out*/DexFile::ClassDef const** final_dex_cache ATTRIBUTE_UNUSED)
+ OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::string location(initial_dex_file.GetLocation());
+ std::string event =
+ std::string("PreDefine:") + descriptor + " <" +
+ location.substr(location.rfind("/") + 1, location.size()) + ">";
+ data.push_back(event);
+ }
+
+ void ClassLoad(Handle<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::string tmp;
+ std::string event = std::string("Load:") + klass->GetDescriptor(&tmp);
+ data.push_back(event);
+ }
+
+ void ClassPrepare(Handle<mirror::Class> temp_klass,
+ Handle<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::string tmp, tmp2;
+ std::string event = std::string("Prepare:") + klass->GetDescriptor(&tmp)
+ + "[" + temp_klass->GetDescriptor(&tmp2) + "]";
+ data.push_back(event);
+ }
+
+ std::vector<std::string> data;
+ };
+
+ Callback cb_;
+};
+
+TEST_F(ClassLoadCallbackRuntimeCallbacksTest, ClassLoadCallback) {
+ ScopedObjectAccess soa(Thread::Current());
+ jobject jclass_loader = LoadDex("XandY");
+ VariableSizedHandleScope hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+ soa.Decode<mirror::ClassLoader>(jclass_loader)));
+
+ const char* descriptor_y = "LY;";
+ Handle<mirror::Class> h_Y(
+ hs.NewHandle(class_linker_->FindClass(soa.Self(), descriptor_y, class_loader)));
+ ASSERT_TRUE(h_Y.Get() != nullptr);
+
+ bool expect1 = Expect({ "PreDefine:LY; <art-gtest-XandY.jar>",
+ "PreDefine:LX; <art-gtest-XandY.jar>",
+ "Load:LX;",
+ "Prepare:LX;[LX;]",
+ "Load:LY;",
+ "Prepare:LY;[LY;]" });
+ EXPECT_TRUE(expect1);
+
+ cb_.data.clear();
+
+ ASSERT_TRUE(class_linker_->EnsureInitialized(Thread::Current(), h_Y, true, true));
+
+ bool expect2 = Expect({ "PreDefine:LY$Z; <art-gtest-XandY.jar>",
+ "Load:LY$Z;",
+ "Prepare:LY$Z;[LY$Z;]" });
+ EXPECT_TRUE(expect2);
+}
+
+class RuntimeSigQuitCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest {
+ protected:
+ void AddListener() OVERRIDE REQUIRES(Locks::mutator_lock_) {
+ Runtime::Current()->GetRuntimeCallbacks()->AddRuntimeSigQuitCallback(&cb_);
+ }
+ void RemoveListener() OVERRIDE REQUIRES(Locks::mutator_lock_) {
+ Runtime::Current()->GetRuntimeCallbacks()->RemoveRuntimeSigQuitCallback(&cb_);
+ }
+
+ struct Callback : public RuntimeSigQuitCallback {
+ void SigQuit() OVERRIDE {
+ ++sigquit_count;
+ }
+
+ size_t sigquit_count = 0;
+ };
+
+ Callback cb_;
+};
+
+TEST_F(RuntimeSigQuitCallbackRuntimeCallbacksTest, SigQuit) {
+ // The runtime needs to be started for the signal handler.
+ Thread* self = Thread::Current();
+
+ self->TransitionFromSuspendedToRunnable();
+ bool started = runtime_->Start();
+ ASSERT_TRUE(started);
+
+ EXPECT_EQ(0u, cb_.sigquit_count);
+
+ kill(getpid(), SIGQUIT);
+
+ // Try a few times.
+ for (size_t i = 0; i != 30; ++i) {
+ if (cb_.sigquit_count == 0) {
+ sleep(1);
+ } else {
+ break;
+ }
+ }
+ EXPECT_EQ(1u, cb_.sigquit_count);
+}
+
+class RuntimePhaseCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest {
+ protected:
+ void AddListener() OVERRIDE REQUIRES(Locks::mutator_lock_) {
+ Runtime::Current()->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&cb_);
+ }
+ void RemoveListener() OVERRIDE REQUIRES(Locks::mutator_lock_) {
+ Runtime::Current()->GetRuntimeCallbacks()->RemoveRuntimePhaseCallback(&cb_);
+ }
+
+ void TearDown() OVERRIDE {
+ // Bypass RuntimeCallbacksTest::TearDown, as the runtime is already gone.
+ CommonRuntimeTest::TearDown();
+ }
+
+ struct Callback : public RuntimePhaseCallback {
+ void NextRuntimePhase(RuntimePhaseCallback::RuntimePhase p) OVERRIDE {
+ if (p == RuntimePhaseCallback::RuntimePhase::kInitialAgents) {
+ if (start_seen > 0 || init_seen > 0 || death_seen > 0) {
+ LOG(FATAL) << "Unexpected order";
+ }
+ ++initial_agents_seen;
+ } else if (p == RuntimePhaseCallback::RuntimePhase::kStart) {
+ if (init_seen > 0 || death_seen > 0) {
+ LOG(FATAL) << "Init seen before start.";
+ }
+ ++start_seen;
+ } else if (p == RuntimePhaseCallback::RuntimePhase::kInit) {
+ ++init_seen;
+ } else if (p == RuntimePhaseCallback::RuntimePhase::kDeath) {
+ ++death_seen;
+ } else {
+ LOG(FATAL) << "Unknown phase " << static_cast<uint32_t>(p);
+ }
+ }
+
+ size_t initial_agents_seen = 0;
+ size_t start_seen = 0;
+ size_t init_seen = 0;
+ size_t death_seen = 0;
+ };
+
+ Callback cb_;
+};
+
+TEST_F(RuntimePhaseCallbackRuntimeCallbacksTest, Phases) {
+ ASSERT_EQ(0u, cb_.initial_agents_seen);
+ ASSERT_EQ(0u, cb_.start_seen);
+ ASSERT_EQ(0u, cb_.init_seen);
+ ASSERT_EQ(0u, cb_.death_seen);
+
+ // Start the runtime.
+ {
+ Thread* self = Thread::Current();
+ self->TransitionFromSuspendedToRunnable();
+ bool started = runtime_->Start();
+ ASSERT_TRUE(started);
+ }
+
+ ASSERT_EQ(0u, cb_.initial_agents_seen);
+ ASSERT_EQ(1u, cb_.start_seen);
+ ASSERT_EQ(1u, cb_.init_seen);
+ ASSERT_EQ(0u, cb_.death_seen);
+
+ // Delete the runtime.
+ runtime_.reset();
+
+ ASSERT_EQ(0u, cb_.initial_agents_seen);
+ ASSERT_EQ(1u, cb_.start_seen);
+ ASSERT_EQ(1u, cb_.init_seen);
+ ASSERT_EQ(1u, cb_.death_seen);
+}
+
+} // namespace art
diff --git a/runtime/runtime_options.cc b/runtime/runtime_options.cc
index e75481c..aa14719 100644
--- a/runtime/runtime_options.cc
+++ b/runtime/runtime_options.cc
@@ -21,6 +21,7 @@
#include "gc/heap.h"
#include "monitor.h"
#include "runtime.h"
+#include "thread_list.h"
#include "trace.h"
#include "utils.h"
#include "debugger.h"
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index ecabf9a..749a36e 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -60,6 +60,8 @@
LongPauseLogThreshold, gc::Heap::kDefaultLongPauseLogThreshold)
RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \
LongGCLogThreshold, gc::Heap::kDefaultLongGCLogThreshold)
+RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \
+ ThreadSuspendTimeout, ThreadList::kDefaultThreadSuspendTimeout)
RUNTIME_OPTIONS_KEY (Unit, DumpGCPerformanceOnShutdown)
RUNTIME_OPTIONS_KEY (Unit, DumpJITInfoOnShutdown)
RUNTIME_OPTIONS_KEY (Unit, IgnoreMaxFootprint)
diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc
index 9ebf9a7..690b069 100644
--- a/runtime/stack_map.cc
+++ b/runtime/stack_map.cc
@@ -116,7 +116,8 @@
void CodeInfo::Dump(VariableIndentationOutputStream* vios,
uint32_t code_offset,
uint16_t number_of_dex_registers,
- bool dump_stack_maps) const {
+ bool dump_stack_maps,
+ InstructionSet instruction_set) const {
CodeInfoEncoding encoding = ExtractEncoding();
size_t number_of_stack_maps = GetNumberOfStackMaps(encoding);
vios->Stream()
@@ -139,6 +140,7 @@
encoding,
code_offset,
number_of_dex_registers,
+ instruction_set,
" " + std::to_string(i));
}
}
@@ -188,14 +190,17 @@
const CodeInfoEncoding& encoding,
uint32_t code_offset,
uint16_t number_of_dex_registers,
+ InstructionSet instruction_set,
const std::string& header_suffix) const {
StackMapEncoding stack_map_encoding = encoding.stack_map_encoding;
+ const uint32_t pc_offset = GetNativePcOffset(stack_map_encoding, instruction_set);
vios->Stream()
<< "StackMap" << header_suffix
<< std::hex
- << " [native_pc=0x" << code_offset + GetNativePcOffset(stack_map_encoding) << "]"
+ << " [native_pc=0x" << code_offset + pc_offset << "]"
+ << " [entry_size=0x" << encoding.stack_map_size_in_bytes << "]"
<< " (dex_pc=0x" << GetDexPc(stack_map_encoding)
- << ", native_pc_offset=0x" << GetNativePcOffset(stack_map_encoding)
+ << ", native_pc_offset=0x" << pc_offset
<< ", dex_register_map_offset=0x" << GetDexRegisterMapOffset(stack_map_encoding)
<< ", inline_info_offset=0x" << GetInlineDescriptorOffset(stack_map_encoding)
<< ", register_mask=0x" << GetRegisterMask(stack_map_encoding)
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 13886f2..cd9a3f0 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -17,6 +17,7 @@
#ifndef ART_RUNTIME_STACK_MAP_H_
#define ART_RUNTIME_STACK_MAP_H_
+#include "arch/code_offset.h"
#include "base/bit_vector.h"
#include "base/bit_utils.h"
#include "dex_file.h"
@@ -805,12 +806,16 @@
encoding.GetDexPcEncoding().Store(region_, dex_pc);
}
- ALWAYS_INLINE uint32_t GetNativePcOffset(const StackMapEncoding& encoding) const {
- return encoding.GetNativePcEncoding().Load(region_);
+ ALWAYS_INLINE uint32_t GetNativePcOffset(const StackMapEncoding& encoding,
+ InstructionSet instruction_set) const {
+ CodeOffset offset(
+ CodeOffset::FromCompressedOffset(encoding.GetNativePcEncoding().Load(region_)));
+ return offset.Uint32Value(instruction_set);
}
- ALWAYS_INLINE void SetNativePcOffset(const StackMapEncoding& encoding, uint32_t native_pc_offset) {
- encoding.GetNativePcEncoding().Store(region_, native_pc_offset);
+ ALWAYS_INLINE void SetNativePcCodeOffset(const StackMapEncoding& encoding,
+ CodeOffset native_pc_offset) {
+ encoding.GetNativePcEncoding().Store(region_, native_pc_offset.CompressedValue());
}
ALWAYS_INLINE uint32_t GetDexRegisterMapOffset(const StackMapEncoding& encoding) const {
@@ -866,6 +871,7 @@
const CodeInfoEncoding& encoding,
uint32_t code_offset,
uint16_t number_of_dex_registers,
+ InstructionSet instruction_set,
const std::string& header_suffix = "") const;
// Special (invalid) offset for the DexRegisterMapOffset field meaning
@@ -1176,6 +1182,17 @@
}
}
+ size_t GetDexRegisterMapsSize(const CodeInfoEncoding& encoding,
+ uint32_t number_of_dex_registers) const {
+ size_t total = 0;
+ for (size_t i = 0, e = GetNumberOfStackMaps(encoding); i < e; ++i) {
+ StackMap stack_map = GetStackMapAt(i, encoding);
+ DexRegisterMap map(GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers));
+ total += map.Size();
+ }
+ return total;
+ }
+
// Return the `DexRegisterMap` pointed by `inline_info` at depth `depth`.
DexRegisterMap GetDexRegisterMapAtDepth(uint8_t depth,
InlineInfo inline_info,
@@ -1234,15 +1251,16 @@
if (stack_map.GetDexPc(stack_map_encoding) == dex_pc) {
StackMap other = GetStackMapAt(i + 1, encoding);
if (other.GetDexPc(stack_map_encoding) == dex_pc &&
- other.GetNativePcOffset(stack_map_encoding) ==
- stack_map.GetNativePcOffset(stack_map_encoding)) {
+ other.GetNativePcOffset(stack_map_encoding, kRuntimeISA) ==
+ stack_map.GetNativePcOffset(stack_map_encoding, kRuntimeISA)) {
DCHECK_EQ(other.GetDexRegisterMapOffset(stack_map_encoding),
stack_map.GetDexRegisterMapOffset(stack_map_encoding));
DCHECK(!stack_map.HasInlineInfo(stack_map_encoding));
if (i < e - 2) {
// Make sure there are not three identical stack maps following each other.
- DCHECK_NE(stack_map.GetNativePcOffset(stack_map_encoding),
- GetStackMapAt(i + 2, encoding).GetNativePcOffset(stack_map_encoding));
+ DCHECK_NE(
+ stack_map.GetNativePcOffset(stack_map_encoding, kRuntimeISA),
+ GetStackMapAt(i + 2, encoding).GetNativePcOffset(stack_map_encoding, kRuntimeISA));
}
return stack_map;
}
@@ -1258,7 +1276,8 @@
// we could do binary search.
for (size_t i = 0, e = GetNumberOfStackMaps(encoding); i < e; ++i) {
StackMap stack_map = GetStackMapAt(i, encoding);
- if (stack_map.GetNativePcOffset(encoding.stack_map_encoding) == native_pc_offset) {
+ if (stack_map.GetNativePcOffset(encoding.stack_map_encoding, kRuntimeISA) ==
+ native_pc_offset) {
return stack_map;
}
}
@@ -1273,7 +1292,8 @@
void Dump(VariableIndentationOutputStream* vios,
uint32_t code_offset,
uint16_t number_of_dex_registers,
- bool dump_stack_maps) const;
+ bool dump_stack_maps,
+ InstructionSet instruction_set) const;
// Check that the code info has valid stack map and abort if it does not.
void AssertValidStackMap(const CodeInfoEncoding& encoding) const {
diff --git a/runtime/thread.cc b/runtime/thread.cc
index ebf14c1..d93eab1 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -67,6 +67,7 @@
#include "quick/quick_method_frame_info.h"
#include "reflection.h"
#include "runtime.h"
+#include "runtime_callbacks.h"
#include "scoped_thread_state_change-inl.h"
#include "ScopedLocalRef.h"
#include "ScopedUtfChars.h"
@@ -431,7 +432,8 @@
ArtField* priorityField = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_priority);
self->SetNativePriority(priorityField->GetInt(self->tlsPtr_.opeer));
- Dbg::PostThreadStart(self);
+
+ runtime->GetRuntimeCallbacks()->ThreadStart(self);
// Invoke the 'run' method of our java.lang.Thread.
ObjPtr<mirror::Object> receiver = self->tlsPtr_.opeer;
@@ -723,8 +725,8 @@
return true;
}
-Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_group,
- bool create_peer) {
+template <typename PeerAction>
+Thread* Thread::Attach(const char* thread_name, bool as_daemon, PeerAction peer_action) {
Runtime* runtime = Runtime::Current();
if (runtime == nullptr) {
LOG(ERROR) << "Thread attaching to non-existent runtime: " << thread_name;
@@ -753,32 +755,11 @@
CHECK_NE(self->GetState(), kRunnable);
self->SetState(kNative);
- // If we're the main thread, ClassLinker won't be created until after we're attached,
- // so that thread needs a two-stage attach. Regular threads don't need this hack.
- // In the compiler, all threads need this hack, because no-one's going to be getting
- // a native peer!
- if (create_peer) {
- self->CreatePeer(thread_name, as_daemon, thread_group);
- if (self->IsExceptionPending()) {
- // We cannot keep the exception around, as we're deleting self. Try to be helpful and log it.
- {
- ScopedObjectAccess soa(self);
- LOG(ERROR) << "Exception creating thread peer:";
- LOG(ERROR) << self->GetException()->Dump();
- self->ClearException();
- }
- runtime->GetThreadList()->Unregister(self);
- // Unregister deletes self, no need to do this here.
- return nullptr;
- }
- } else {
- // These aren't necessary, but they improve diagnostics for unit tests & command-line tools.
- if (thread_name != nullptr) {
- self->tlsPtr_.name->assign(thread_name);
- ::art::SetThreadName(thread_name);
- } else if (self->GetJniEnv()->check_jni) {
- LOG(WARNING) << *Thread::Current() << " attached without supplying a name";
- }
+ // Run the action that is acting on the peer.
+ if (!peer_action(self)) {
+ runtime->GetThreadList()->Unregister(self);
+ // Unregister deletes self, no need to do this here.
+ return nullptr;
}
if (VLOG_IS_ON(threads)) {
@@ -793,12 +774,63 @@
{
ScopedObjectAccess soa(self);
- Dbg::PostThreadStart(self);
+ runtime->GetRuntimeCallbacks()->ThreadStart(self);
}
return self;
}
+Thread* Thread::Attach(const char* thread_name,
+ bool as_daemon,
+ jobject thread_group,
+ bool create_peer) {
+ auto create_peer_action = [&](Thread* self) {
+ // If we're the main thread, ClassLinker won't be created until after we're attached,
+ // so that thread needs a two-stage attach. Regular threads don't need this hack.
+ // In the compiler, all threads need this hack, because no-one's going to be getting
+ // a native peer!
+ if (create_peer) {
+ self->CreatePeer(thread_name, as_daemon, thread_group);
+ if (self->IsExceptionPending()) {
+ // We cannot keep the exception around, as we're deleting self. Try to be helpful and log it.
+ {
+ ScopedObjectAccess soa(self);
+ LOG(ERROR) << "Exception creating thread peer:";
+ LOG(ERROR) << self->GetException()->Dump();
+ self->ClearException();
+ }
+ return false;
+ }
+ } else {
+ // These aren't necessary, but they improve diagnostics for unit tests & command-line tools.
+ if (thread_name != nullptr) {
+ self->tlsPtr_.name->assign(thread_name);
+ ::art::SetThreadName(thread_name);
+ } else if (self->GetJniEnv()->check_jni) {
+ LOG(WARNING) << *Thread::Current() << " attached without supplying a name";
+ }
+ }
+ return true;
+ };
+ return Attach(thread_name, as_daemon, create_peer_action);
+}
+
+Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_peer) {
+ auto set_peer_action = [&](Thread* self) {
+ // Install the given peer.
+ {
+ DCHECK(self == Thread::Current());
+ ScopedObjectAccess soa(self);
+ self->tlsPtr_.opeer = soa.Decode<mirror::Object>(thread_peer).Ptr();
+ }
+ self->GetJniEnv()->SetLongField(thread_peer,
+ WellKnownClasses::java_lang_Thread_nativePeer,
+ reinterpret_cast<jlong>(self));
+ return true;
+ };
+ return Attach(thread_name, as_daemon, set_peer_action);
+}
+
void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group) {
Runtime* runtime = Runtime::Current();
CHECK(runtime->IsStarted());
@@ -1929,7 +1961,11 @@
jni::DecodeArtField(WellKnownClasses::java_lang_Thread_nativePeer)
->SetLong<false>(tlsPtr_.opeer, 0);
}
- Dbg::PostThreadDeath(self);
+ Runtime* runtime = Runtime::Current();
+ if (runtime != nullptr) {
+ runtime->GetRuntimeCallbacks()->ThreadDeath(self);
+ }
+
// Thread.join() is implemented as an Object.wait() on the Thread.lock object. Signal anyone
// who is waiting.
diff --git a/runtime/thread.h b/runtime/thread.h
index 2b451bc..b609e72 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -158,6 +158,8 @@
// Used to implement JNI AttachCurrentThread and AttachCurrentThreadAsDaemon calls.
static Thread* Attach(const char* thread_name, bool as_daemon, jobject thread_group,
bool create_peer);
+ // Attaches the calling native thread to the runtime, returning the new native peer.
+ static Thread* Attach(const char* thread_name, bool as_daemon, jobject thread_peer);
// Reset internal state of child thread after fork.
void InitAfterFork();
@@ -1166,6 +1168,13 @@
~Thread() REQUIRES(!Locks::mutator_lock_, !Locks::thread_suspend_count_lock_);
void Destroy();
+ // Attaches the calling native thread to the runtime, returning the new native peer.
+ // Used to implement JNI AttachCurrentThread and AttachCurrentThreadAsDaemon calls.
+ template <typename PeerAction>
+ static Thread* Attach(const char* thread_name,
+ bool as_daemon,
+ PeerAction p);
+
void CreatePeer(const char* name, bool as_daemon, jobject thread_group);
template<bool kTransactionActive>
@@ -1704,6 +1713,14 @@
Thread* const self_;
};
+class ThreadLifecycleCallback {
+ public:
+ virtual ~ThreadLifecycleCallback() {}
+
+ virtual void ThreadStart(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+ virtual void ThreadDeath(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+};
+
std::ostream& operator<<(std::ostream& os, const Thread& thread);
std::ostream& operator<<(std::ostream& os, const StackedShadowFrameType& thread);
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index c5c7e2c..01c940e 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -57,7 +57,6 @@
using android::base::StringPrintf;
static constexpr uint64_t kLongThreadSuspendThreshold = MsToNs(5);
-static constexpr uint64_t kThreadSuspendTimeoutMs = 30 * 1000; // 30s.
// Use 0 since we want to yield to prevent blocking for an unpredictable amount of time.
static constexpr useconds_t kThreadSuspendInitialSleepUs = 0;
static constexpr useconds_t kThreadSuspendMaxYieldUs = 3000;
@@ -68,12 +67,13 @@
// Turned off again. b/29248079
static constexpr bool kDumpUnattachedThreadNativeStackForSigQuit = false;
-ThreadList::ThreadList()
+ThreadList::ThreadList(uint64_t thread_suspend_timeout_ns)
: suspend_all_count_(0),
debug_suspend_all_count_(0),
unregistering_count_(0),
suspend_all_historam_("suspend all histogram", 16, 64),
long_suspend_(false),
+ thread_suspend_timeout_ns_(thread_suspend_timeout_ns),
empty_checkpoint_barrier_(new Barrier(0)) {
CHECK(Monitor::IsValidLockWord(LockWord::FromThinLockId(kMaxThreadId, 1, 0U)));
}
@@ -554,12 +554,14 @@
// Make sure this thread grabs exclusive access to the mutator lock and its protected data.
#if HAVE_TIMED_RWLOCK
while (true) {
- if (Locks::mutator_lock_->ExclusiveLockWithTimeout(self, kThreadSuspendTimeoutMs, 0)) {
+ if (Locks::mutator_lock_->ExclusiveLockWithTimeout(self,
+ NsToMs(thread_suspend_timeout_ns_),
+ 0)) {
break;
} else if (!long_suspend_) {
// Reading long_suspend without the mutator lock is slightly racy, in some rare cases, this
// could result in a thread suspend timeout.
- // Timeout if we wait more than kThreadSuspendTimeoutMs seconds.
+ // Timeout if we wait more than thread_suspend_timeout_ns_ nanoseconds.
UnsafeLogFatalForThreadSuspendAllTimeout();
}
}
@@ -653,7 +655,7 @@
// is done with a timeout so that we can detect problems.
#if ART_USE_FUTEXES
timespec wait_timeout;
- InitTimeSpec(false, CLOCK_MONOTONIC, kIsDebugBuild ? 50000 : 10000, 0, &wait_timeout);
+ InitTimeSpec(false, CLOCK_MONOTONIC, NsToMs(thread_suspend_timeout_ns_), 0, &wait_timeout);
#endif
const uint64_t start_time = NanoTime();
while (true) {
@@ -863,7 +865,7 @@
return thread;
}
const uint64_t total_delay = NanoTime() - start_time;
- if (total_delay >= MsToNs(kThreadSuspendTimeoutMs)) {
+ if (total_delay >= thread_suspend_timeout_ns_) {
ThreadSuspendByPeerWarning(self,
::android::base::FATAL,
"Thread suspension timed out",
@@ -969,7 +971,7 @@
return thread;
}
const uint64_t total_delay = NanoTime() - start_time;
- if (total_delay >= MsToNs(kThreadSuspendTimeoutMs)) {
+ if (total_delay >= thread_suspend_timeout_ns_) {
ThreadSuspendByThreadIdWarning(::android::base::WARNING,
"Thread suspension timed out",
thread_id);
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 658db00..b60fca1 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -20,6 +20,7 @@
#include "barrier.h"
#include "base/histogram.h"
#include "base/mutex.h"
+#include "base/time_utils.h"
#include "base/value_object.h"
#include "gc_root.h"
#include "jni.h"
@@ -41,11 +42,12 @@
class ThreadList {
public:
- static const uint32_t kMaxThreadId = 0xFFFF;
- static const uint32_t kInvalidThreadId = 0;
- static const uint32_t kMainThreadId = 1;
+ static constexpr uint32_t kMaxThreadId = 0xFFFF;
+ static constexpr uint32_t kInvalidThreadId = 0;
+ static constexpr uint32_t kMainThreadId = 1;
+ static constexpr uint64_t kDefaultThreadSuspendTimeout = MsToNs(kIsDebugBuild ? 50000 : 10000);
- explicit ThreadList();
+ explicit ThreadList(uint64_t thread_suspend_timeout_ns);
~ThreadList();
void DumpForSigQuit(std::ostream& os)
@@ -219,6 +221,9 @@
// Whether or not the current thread suspension is long.
bool long_suspend_;
+ // Thread suspension timeout in nanoseconds.
+ const uint64_t thread_suspend_timeout_ns_;
+
std::unique_ptr<Barrier> empty_checkpoint_barrier_;
friend class Thread;
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 9d9360e..2add955 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -54,6 +54,7 @@
static_cast<size_t>(kTraceMethodActionMask));
static constexpr uint8_t kOpNewMethod = 1U;
static constexpr uint8_t kOpNewThread = 2U;
+static constexpr uint8_t kOpTraceSummary = 3U;
class BuildStackTraceVisitor : public StackVisitor {
public:
@@ -700,20 +701,19 @@
std::string header(os.str());
if (trace_output_mode_ == TraceOutputMode::kStreaming) {
- File file(streaming_file_name_ + ".sec", O_CREAT | O_WRONLY, true);
- if (!file.IsOpened()) {
- LOG(WARNING) << "Could not open secondary trace file!";
- return;
- }
- if (!file.WriteFully(header.c_str(), header.length())) {
- file.Erase();
- std::string detail(StringPrintf("Trace data write failed: %s", strerror(errno)));
- PLOG(ERROR) << detail;
- ThrowRuntimeException("%s", detail.c_str());
- }
- if (file.FlushCloseOrErase() != 0) {
- PLOG(ERROR) << "Could not write secondary file";
- }
+ MutexLock mu(Thread::Current(), *streaming_lock_); // To serialize writing.
+ // Write a special token to mark the end of trace records and the start of
+ // trace summary.
+ uint8_t buf[7];
+ Append2LE(buf, 0);
+ buf[2] = kOpTraceSummary;
+ Append4LE(buf + 3, static_cast<uint32_t>(header.length()));
+ WriteToBuf(buf, sizeof(buf));
+ // Write the trace summary. The summary is identical to the file header when
+ // the output mode is not streaming (except for methods).
+ WriteToBuf(reinterpret_cast<const uint8_t*>(header.c_str()), header.length());
+ // Flush the buffer, which may include some trace records before the summary.
+ FlushBuf();
} else {
if (trace_file_.get() == nullptr) {
iovec iov[2];
@@ -894,6 +894,14 @@
memcpy(buf_.get() + old_offset, src, src_size);
}
+void Trace::FlushBuf() {
+ int32_t offset = cur_offset_.LoadRelaxed();
+ if (!trace_file_->WriteFully(buf_.get(), offset)) {
+ PLOG(WARNING) << "Failed flush the remaining data in streaming.";
+ }
+ cur_offset_.StoreRelease(0);
+}
+
void Trace::LogMethodTraceEvent(Thread* thread, ArtMethod* method,
instrumentation::Instrumentation::InstrumentationEvent event,
uint32_t thread_clock_diff, uint32_t wall_clock_diff) {
diff --git a/runtime/trace.h b/runtime/trace.h
index 824b150..485e9a1 100644
--- a/runtime/trace.h
+++ b/runtime/trace.h
@@ -202,7 +202,8 @@
// This causes the negative annotations to incorrectly have a false positive. TODO: Figure out
// how to annotate this.
NO_THREAD_SAFETY_ANALYSIS;
- void FinishTracing() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_);
+ void FinishTracing()
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_, !*streaming_lock_);
void ReadClocks(Thread* thread, uint32_t* thread_clock_diff, uint32_t* wall_clock_diff);
@@ -229,6 +230,9 @@
// annotation.
void WriteToBuf(const uint8_t* src, size_t src_size)
REQUIRES(streaming_lock_);
+ // Flush the main buffer to file. Used for streaming. Exposed here for lock annotation.
+ void FlushBuf()
+ REQUIRES(streaming_lock_);
uint32_t EncodeTraceMethod(ArtMethod* method) REQUIRES(!*unique_methods_lock_);
uint32_t EncodeTraceMethodAndAction(ArtMethod* method, TraceAction action)
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index 15cc566..1131607 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -963,20 +963,25 @@
// Check recorded fields are resolved the same way, have the same recorded class,
// and have the same recorded flags.
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- StackHandleScope<1> hs(self);
- Handle<mirror::DexCache> dex_cache(
- hs.NewHandle(class_linker->FindDexCache(self, dex_file, /* allow_failure */ false)));
for (const auto& entry : fields) {
- ArtField* field = class_linker->ResolveFieldJLS(
- dex_file, entry.GetDexFieldIndex(), dex_cache, class_loader);
-
- if (field == nullptr) {
- DCHECK(self->IsExceptionPending());
- self->ClearException();
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(entry.GetDexFieldIndex());
+ StringPiece name(dex_file.StringDataByIdx(field_id.name_idx_));
+ StringPiece type(dex_file.StringDataByIdx(dex_file.GetTypeId(field_id.type_idx_).descriptor_idx_));
+ // Only use field_id.class_idx_ when the entry is unresolved, which is rare.
+ // Otherwise, we might end up resolving an application class, which is expensive.
+ std::string expected_decl_klass = entry.IsResolved()
+ ? GetStringFromId(dex_file, entry.GetDeclaringClassIndex())
+ : dex_file.StringByTypeIdx(field_id.class_idx_);
+ mirror::Class* cls = FindClassAndClearException(
+ class_linker, self, expected_decl_klass.c_str(), class_loader);
+ if (cls == nullptr) {
+ LOG(INFO) << "VerifierDeps: Could not resolve class " << expected_decl_klass;
+ return false;
}
+ DCHECK(cls->IsResolved());
+ ArtField* field = mirror::Class::FindField(self, cls, name, type);
if (entry.IsResolved()) {
- std::string expected_decl_klass = GetStringFromId(dex_file, entry.GetDeclaringClassIndex());
std::string temp;
if (field == nullptr) {
LOG(INFO) << "VerifierDeps: Could not resolve field "
@@ -1025,11 +1030,16 @@
const char* name = dex_file.GetMethodName(method_id);
const Signature signature = dex_file.GetMethodSignature(method_id);
- const char* descriptor = dex_file.GetMethodDeclaringClassDescriptor(method_id);
+ // Only use method_id.class_idx_ when the entry is unresolved, which is rare.
+ // Otherwise, we might end up resolving an application class, which is expensive.
+ std::string expected_decl_klass = entry.IsResolved()
+ ? GetStringFromId(dex_file, entry.GetDeclaringClassIndex())
+ : dex_file.StringByTypeIdx(method_id.class_idx_);
- mirror::Class* cls = FindClassAndClearException(class_linker, self, descriptor, class_loader);
+ mirror::Class* cls = FindClassAndClearException(
+ class_linker, self, expected_decl_klass.c_str(), class_loader);
if (cls == nullptr) {
- LOG(INFO) << "VerifierDeps: Could not resolve class " << descriptor;
+ LOG(INFO) << "VerifierDeps: Could not resolve class " << expected_decl_klass;
return false;
}
DCHECK(cls->IsResolved());
@@ -1045,7 +1055,6 @@
if (entry.IsResolved()) {
std::string temp;
- std::string expected_decl_klass = GetStringFromId(dex_file, entry.GetDeclaringClassIndex());
if (method == nullptr) {
LOG(INFO) << "VerifierDeps: Could not resolve "
<< kind
diff --git a/test/595-profile-saving/profile-saving.cc b/test/595-profile-saving/profile-saving.cc
index bf3d812..0f8dd57 100644
--- a/test/595-profile-saving/profile-saving.cc
+++ b/test/595-profile-saving/profile-saving.cc
@@ -17,7 +17,7 @@
#include "dex_file.h"
#include "art_method-inl.h"
-#include "jit/offline_profiling_info.h"
+#include "jit/profile_compilation_info.h"
#include "jit/profile_saver.h"
#include "jni.h"
#include "method_reference.h"
diff --git a/test/596-monitor-inflation/expected.txt b/test/596-monitor-inflation/expected.txt
new file mode 100644
index 0000000..2add696
--- /dev/null
+++ b/test/596-monitor-inflation/expected.txt
@@ -0,0 +1,6 @@
+JNI_OnLoad called
+Monitor list grew by at least 4000 monitors
+Monitor list shrank correctly
+Finished first check
+Finished second check
+Total checks: 10000
diff --git a/test/596-monitor-inflation/info.txt b/test/596-monitor-inflation/info.txt
new file mode 100644
index 0000000..81dedb6
--- /dev/null
+++ b/test/596-monitor-inflation/info.txt
@@ -0,0 +1,5 @@
+A simple test that forces many monitors to be inflated, while checking
+that hashcodes are consistently maintained.
+
+This allocates more monitors and hence may exercise the monitor pool
+differently, and with more context, than the monitor_pool_test gtest.
diff --git a/test/596-monitor-inflation/monitor_inflation.cc b/test/596-monitor-inflation/monitor_inflation.cc
new file mode 100644
index 0000000..fb4275b
--- /dev/null
+++ b/test/596-monitor-inflation/monitor_inflation.cc
@@ -0,0 +1,35 @@
+/*
+ * 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 "gc/heap.h"
+#include "jni.h"
+#include "monitor.h"
+#include "runtime.h"
+#include "thread-inl.h"
+
+namespace art {
+namespace {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_trim(JNIEnv*, jclass) {
+ Runtime::Current()->GetHeap()->Trim(Thread::Current());
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_monitorListSize(JNIEnv*, jclass) {
+ return Runtime::Current()->GetMonitorList()->Size();
+}
+
+} // namespace
+} // namespace art
diff --git a/test/596-monitor-inflation/src/Main.java b/test/596-monitor-inflation/src/Main.java
new file mode 100644
index 0000000..d97c766
--- /dev/null
+++ b/test/596-monitor-inflation/src/Main.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import java.util.IdentityHashMap;
+import dalvik.system.VMRuntime;
+
+public class Main {
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ int initialSize = monitorListSize();
+ IdentityHashMap<Object, Integer> all = new IdentityHashMap();
+ for (int i = 0; i < 5000; ++i) {
+ Object obj = new Object();
+ synchronized(obj) {
+ // Should force inflation.
+ all.put(obj, obj.hashCode());
+ }
+ }
+ // Since monitor deflation is delayed significantly, we believe that even with an intervening
+ // GC, monitors should remain inflated. We allow some slop for unrelated concurrent runtime
+ // actions.
+ int inflatedSize = monitorListSize();
+ if (inflatedSize >= initialSize + 4000) {
+ System.out.println("Monitor list grew by at least 4000 monitors");
+ } else {
+ System.out.println("Monitor list did not grow as expected");
+ }
+ // Encourage monitor deflation.
+ // trim() (Heap::Trim()) deflates only in JANK_IMPERCEPTIBLE state.
+ // Some of this mirrors code in ActivityThread.java.
+ final int DALVIK_PROCESS_STATE_JANK_PERCEPTIBLE = 0;
+ final int DALVIK_PROCESS_STATE_JANK_IMPERCEPTIBLE = 1;
+ VMRuntime.getRuntime().updateProcessState(DALVIK_PROCESS_STATE_JANK_IMPERCEPTIBLE);
+ System.gc();
+ System.runFinalization();
+ trim();
+ VMRuntime.getRuntime().updateProcessState(DALVIK_PROCESS_STATE_JANK_PERCEPTIBLE);
+ int finalSize = monitorListSize();
+ if (finalSize > initialSize + 1000) {
+ System.out.println("Monitor list failed to shrink properly");
+ } else {
+ System.out.println("Monitor list shrank correctly");
+ }
+ int j = 0;
+ for (Object obj: all.keySet()) {
+ ++j;
+ if (obj.hashCode() != all.get(obj)) {
+ throw new AssertionError("Failed hashcode test!");
+ }
+ }
+ System.out.println("Finished first check");
+ for (Object obj: all.keySet()) {
+ ++j;
+ synchronized(obj) {
+ if (obj.hashCode() != all.get(obj)) {
+ throw new AssertionError("Failed hashcode test!");
+ }
+ }
+ }
+ System.out.println("Finished second check");
+ System.out.println("Total checks: " + j);
+ }
+
+ private static native void trim();
+
+ private static native int monitorListSize();
+}
diff --git a/test/901-hello-ti-agent/basics.cc b/test/901-hello-ti-agent/basics.cc
index 052fb9a..0b17656 100644
--- a/test/901-hello-ti-agent/basics.cc
+++ b/test/901-hello-ti-agent/basics.cc
@@ -28,6 +28,46 @@
namespace art {
namespace Test901HelloTi {
+static void EnableEvent(jvmtiEnv* env, jvmtiEvent evt) {
+ jvmtiError error = env->SetEventNotificationMode(JVMTI_ENABLE, evt, nullptr);
+ if (error != JVMTI_ERROR_NONE) {
+ printf("Failed to enable event");
+ }
+}
+
+static void JNICALL VMStartCallback(jvmtiEnv *jenv ATTRIBUTE_UNUSED,
+ JNIEnv* jni_env ATTRIBUTE_UNUSED) {
+ printf("VMStart\n");
+}
+
+static void JNICALL VMInitCallback(jvmtiEnv *jvmti_env ATTRIBUTE_UNUSED,
+ JNIEnv* jni_env ATTRIBUTE_UNUSED,
+ jthread thread ATTRIBUTE_UNUSED) {
+ printf("VMInit\n");
+}
+
+static void JNICALL VMDeatchCallback(jvmtiEnv *jenv ATTRIBUTE_UNUSED,
+ JNIEnv* jni_env ATTRIBUTE_UNUSED) {
+ printf("VMDeath\n");
+}
+
+
+static void InstallVMEvents(jvmtiEnv* env) {
+ jvmtiEventCallbacks callbacks;
+ memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
+ callbacks.VMStart = VMStartCallback;
+ callbacks.VMInit = VMInitCallback;
+ callbacks.VMDeath = VMDeatchCallback;
+ jvmtiError ret = env->SetEventCallbacks(&callbacks, sizeof(callbacks));
+ if (ret != JVMTI_ERROR_NONE) {
+ printf("Failed to install callbacks");
+ }
+
+ EnableEvent(env, JVMTI_EVENT_VM_START);
+ EnableEvent(env, JVMTI_EVENT_VM_INIT);
+ EnableEvent(env, JVMTI_EVENT_VM_DEATH);
+}
+
jint OnLoad(JavaVM* vm,
char* options ATTRIBUTE_UNUSED,
void* reserved ATTRIBUTE_UNUSED) {
@@ -72,6 +112,10 @@
printf("Unexpected version number!\n");
return -1;
}
+
+ InstallVMEvents(env);
+ InstallVMEvents(env2);
+
CHECK_CALL_SUCCESS(env->DisposeEnvironment());
CHECK_CALL_SUCCESS(env2->DisposeEnvironment());
#undef CHECK_CALL_SUCCESS
@@ -82,6 +126,19 @@
}
SetAllCapabilities(jvmti_env);
+ jvmtiPhase current_phase;
+ jvmtiError phase_result = jvmti_env->GetPhase(¤t_phase);
+ if (phase_result != JVMTI_ERROR_NONE) {
+ printf("Could not get phase");
+ return 1;
+ }
+ if (current_phase != JVMTI_PHASE_ONLOAD) {
+ printf("Wrong phase");
+ return 1;
+ }
+
+ InstallVMEvents(jvmti_env);
+
return JNI_OK;
}
@@ -92,5 +149,15 @@
JvmtiErrorToException(env, result);
}
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_checkLivePhase(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
+ jvmtiPhase current_phase;
+ jvmtiError phase_result = jvmti_env->GetPhase(¤t_phase);
+ if (JvmtiErrorToException(env, phase_result)) {
+ return JNI_FALSE;
+ }
+ return (current_phase == JVMTI_PHASE_LIVE) ? JNI_TRUE : JNI_FALSE;
+}
+
} // namespace Test901HelloTi
} // namespace art
diff --git a/test/901-hello-ti-agent/expected.txt b/test/901-hello-ti-agent/expected.txt
index 2aee99b..c4b24cb 100644
--- a/test/901-hello-ti-agent/expected.txt
+++ b/test/901-hello-ti-agent/expected.txt
@@ -1,8 +1,12 @@
Loaded Agent for test 901-hello-ti-agent
+VMStart
+VMInit
Hello, world!
+Agent in live phase.
0
1
2
4
8
JVMTI_ERROR_ILLEGAL_ARGUMENT
+VMDeath
diff --git a/test/901-hello-ti-agent/src/Main.java b/test/901-hello-ti-agent/src/Main.java
index 775e5c2..4d62ed3 100644
--- a/test/901-hello-ti-agent/src/Main.java
+++ b/test/901-hello-ti-agent/src/Main.java
@@ -16,10 +16,12 @@
public class Main {
public static void main(String[] args) {
- System.loadLibrary(args[1]);
-
System.out.println("Hello, world!");
+ if (checkLivePhase()) {
+ System.out.println("Agent in live phase.");
+ }
+
set(0); // OTHER
set(1); // GC
set(2); // CLASS
@@ -37,5 +39,6 @@
}
}
+ private static native boolean checkLivePhase();
private static native void setVerboseFlag(int flag, boolean value);
}
diff --git a/test/902-hello-transformation/src/Main.java b/test/902-hello-transformation/src/Main.java
index ec47119..471c82b 100644
--- a/test/902-hello-transformation/src/Main.java
+++ b/test/902-hello-transformation/src/Main.java
@@ -49,7 +49,6 @@
"AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=");
public static void main(String[] args) {
- System.loadLibrary(args[1]);
doTest(new Transform());
}
diff --git a/test/903-hello-tagging/src/Main.java b/test/903-hello-tagging/src/Main.java
index a8aedb4..2f0365a 100644
--- a/test/903-hello-tagging/src/Main.java
+++ b/test/903-hello-tagging/src/Main.java
@@ -20,7 +20,6 @@
public class Main {
public static void main(String[] args) {
- System.loadLibrary(args[1]);
doTest();
testGetTaggedObjects();
}
diff --git a/test/904-object-allocation/src/Main.java b/test/904-object-allocation/src/Main.java
index fc8a112..df59179 100644
--- a/test/904-object-allocation/src/Main.java
+++ b/test/904-object-allocation/src/Main.java
@@ -18,8 +18,6 @@
public class Main {
public static void main(String[] args) throws Exception {
- System.loadLibrary(args[1]);
-
// Use a list to ensure objects must be allocated.
ArrayList<Object> l = new ArrayList<>(100);
diff --git a/test/905-object-free/src/Main.java b/test/905-object-free/src/Main.java
index 16dec5d..e41e378 100644
--- a/test/905-object-free/src/Main.java
+++ b/test/905-object-free/src/Main.java
@@ -19,8 +19,6 @@
public class Main {
public static void main(String[] args) throws Exception {
- System.loadLibrary(args[1]);
-
doTest();
}
diff --git a/test/906-iterate-heap/src/Main.java b/test/906-iterate-heap/src/Main.java
index 544a365..cab27be 100644
--- a/test/906-iterate-heap/src/Main.java
+++ b/test/906-iterate-heap/src/Main.java
@@ -19,8 +19,6 @@
public class Main {
public static void main(String[] args) throws Exception {
- System.loadLibrary(args[1]);
-
doTest();
}
diff --git a/test/907-get-loaded-classes/src/Main.java b/test/907-get-loaded-classes/src/Main.java
index 468d037..370185a 100644
--- a/test/907-get-loaded-classes/src/Main.java
+++ b/test/907-get-loaded-classes/src/Main.java
@@ -20,8 +20,6 @@
public class Main {
public static void main(String[] args) throws Exception {
- System.loadLibrary(args[1]);
-
doTest();
}
diff --git a/test/908-gc-start-finish/src/Main.java b/test/908-gc-start-finish/src/Main.java
index 2be0eea..05388c9 100644
--- a/test/908-gc-start-finish/src/Main.java
+++ b/test/908-gc-start-finish/src/Main.java
@@ -18,8 +18,6 @@
public class Main {
public static void main(String[] args) throws Exception {
- System.loadLibrary(args[1]);
-
doTest();
}
diff --git a/test/910-methods/src/Main.java b/test/910-methods/src/Main.java
index bf25a0d..932a1ea 100644
--- a/test/910-methods/src/Main.java
+++ b/test/910-methods/src/Main.java
@@ -20,8 +20,6 @@
public class Main {
public static void main(String[] args) throws Exception {
- System.loadLibrary(args[1]);
-
doTest();
}
diff --git a/test/911-get-stack-trace/expected.txt b/test/911-get-stack-trace/expected.txt
index dad08c9..2687f85 100644
--- a/test/911-get-stack-trace/expected.txt
+++ b/test/911-get-stack-trace/expected.txt
@@ -22,7 +22,7 @@
bar (IIILControlData;)J 0 24
foo (IIILControlData;)I 0 19
doTest ()V 38 23
- main ([Ljava/lang/String;)V 6 21
+ main ([Ljava/lang/String;)V 3 21
---------
print (Ljava/lang/Thread;II)V 0 34
printOrWait (IILControlData;)V 6 39
@@ -42,7 +42,7 @@
bar (IIILControlData;)J 0 24
foo (IIILControlData;)I 0 19
doTest ()V 42 24
- main ([Ljava/lang/String;)V 6 21
+ main ([Ljava/lang/String;)V 3 21
---------
getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2
print (Ljava/lang/Thread;II)V 0 34
@@ -57,13 +57,13 @@
baz (IIILControlData;)Ljava/lang/Object; 9 32
From bottom
---------
- main ([Ljava/lang/String;)V 6 21
+ main ([Ljava/lang/String;)V 3 21
---------
baz (IIILControlData;)Ljava/lang/Object; 9 32
bar (IIILControlData;)J 0 24
foo (IIILControlData;)I 0 19
doTest ()V 65 30
- main ([Ljava/lang/String;)V 6 21
+ main ([Ljava/lang/String;)V 3 21
---------
bar (IIILControlData;)J 0 24
foo (IIILControlData;)I 0 19
@@ -358,7 +358,7 @@
getAllStackTraces (I)[[Ljava/lang/Object; -1 -2
printAll (I)V 0 73
doTest ()V 102 57
- main ([Ljava/lang/String;)V 30 33
+ main ([Ljava/lang/String;)V 27 33
---------
FinalizerDaemon
@@ -590,7 +590,7 @@
getAllStackTraces (I)[[Ljava/lang/Object; -1 -2
printAll (I)V 0 73
doTest ()V 107 59
- main ([Ljava/lang/String;)V 30 33
+ main ([Ljava/lang/String;)V 27 33
########################################
@@ -659,7 +659,7 @@
getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2
printList ([Ljava/lang/Thread;I)V 0 66
doTest ()V 96 52
- main ([Ljava/lang/String;)V 38 37
+ main ([Ljava/lang/String;)V 35 37
---------
Thread-14
@@ -771,7 +771,7 @@
getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2
printList ([Ljava/lang/Thread;I)V 0 66
doTest ()V 101 54
- main ([Ljava/lang/String;)V 38 37
+ main ([Ljava/lang/String;)V 35 37
###################
@@ -782,7 +782,7 @@
[public static native java.lang.Object[] Frames.getFrameLocation(java.lang.Thread,int), ffffffff]
[public static void Frames.doTestSameThread(), 38]
[public static void Frames.doTest() throws java.lang.Exception, 0]
-[public static void Main.main(java.lang.String[]) throws java.lang.Exception, 2e]
+[public static void Main.main(java.lang.String[]) throws java.lang.Exception, 2b]
JVMTI_ERROR_NO_MORE_FRAMES
################################
diff --git a/test/911-get-stack-trace/src/Main.java b/test/911-get-stack-trace/src/Main.java
index b199033..96a427d 100644
--- a/test/911-get-stack-trace/src/Main.java
+++ b/test/911-get-stack-trace/src/Main.java
@@ -16,7 +16,7 @@
public class Main {
public static void main(String[] args) throws Exception {
- System.loadLibrary(args[1]);
+ bindTest911Classes();
SameThread.doTest();
@@ -42,4 +42,6 @@
System.out.println("Done");
}
+
+ private static native void bindTest911Classes();
}
diff --git a/test/911-get-stack-trace/stack_trace.cc b/test/911-get-stack-trace/stack_trace.cc
index d162e8a..68f6d8d 100644
--- a/test/911-get-stack-trace/stack_trace.cc
+++ b/test/911-get-stack-trace/stack_trace.cc
@@ -34,6 +34,14 @@
using android::base::StringPrintf;
+extern "C" JNIEXPORT void JNICALL Java_Main_bindTest911Classes(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
+ BindFunctions(jvmti_env, env, "AllTraces");
+ BindFunctions(jvmti_env, env, "Frames");
+ BindFunctions(jvmti_env, env, "PrintThread");
+ BindFunctions(jvmti_env, env, "ThreadListTraces");
+}
+
static jint FindLineNumber(jint line_number_count,
jvmtiLineNumberEntry* line_number_table,
jlocation location) {
diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc
index a22d1d7..d13436e 100644
--- a/test/912-classes/classes.cc
+++ b/test/912-classes/classes.cc
@@ -20,6 +20,7 @@
#include "jni.h"
#include "openjdkjvmti/jvmti.h"
#include "ScopedLocalRef.h"
+#include "thread-inl.h"
#include "ti-agent/common_helper.h"
#include "ti-agent/common_load.h"
@@ -241,5 +242,138 @@
return ret;
}
+extern "C" JNIEXPORT jintArray JNICALL Java_Main_getClassVersion(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+ jint major, minor;
+ jvmtiError result = jvmti_env->GetClassVersionNumbers(klass, &minor, &major);
+ if (JvmtiErrorToException(env, result)) {
+ return nullptr;
+ }
+
+ jintArray int_array = env->NewIntArray(2);
+ if (int_array == nullptr) {
+ return nullptr;
+ }
+ jint buf[2] = { major, minor };
+ env->SetIntArrayRegion(int_array, 0, 2, buf);
+
+ return int_array;
+}
+
+static std::string GetClassName(jvmtiEnv* jenv, JNIEnv* jni_env, jclass klass) {
+ char* name;
+ jvmtiError result = jenv->GetClassSignature(klass, &name, nullptr);
+ if (result != JVMTI_ERROR_NONE) {
+ if (jni_env != nullptr) {
+ JvmtiErrorToException(jni_env, result);
+ } else {
+ printf("Failed to get class signature.\n");
+ }
+ return "";
+ }
+
+ std::string tmp(name);
+ jenv->Deallocate(reinterpret_cast<unsigned char*>(name));
+
+ return tmp;
+}
+
+static std::string GetThreadName(jvmtiEnv* jenv, JNIEnv* jni_env, jthread thread) {
+ jvmtiThreadInfo info;
+ jvmtiError result = jenv->GetThreadInfo(thread, &info);
+ if (result != JVMTI_ERROR_NONE) {
+ if (jni_env != nullptr) {
+ JvmtiErrorToException(jni_env, result);
+ } else {
+ printf("Failed to get thread name.\n");
+ }
+ return "";
+ }
+
+ std::string tmp(info.name);
+ jenv->Deallocate(reinterpret_cast<unsigned char*>(info.name));
+ jni_env->DeleteLocalRef(info.context_class_loader);
+ jni_env->DeleteLocalRef(info.thread_group);
+
+ return tmp;
+}
+
+static std::string GetThreadName(Thread* thread) {
+ std::string tmp;
+ thread->GetThreadName(tmp);
+ return tmp;
+}
+
+static void JNICALL ClassPrepareCallback(jvmtiEnv* jenv,
+ JNIEnv* jni_env,
+ jthread thread,
+ jclass klass) {
+ std::string name = GetClassName(jenv, jni_env, klass);
+ if (name == "") {
+ return;
+ }
+ std::string thread_name = GetThreadName(jenv, jni_env, thread);
+ if (thread_name == "") {
+ return;
+ }
+ std::string cur_thread_name = GetThreadName(Thread::Current());
+ printf("Prepare: %s on %s (cur=%s)\n",
+ name.c_str(),
+ thread_name.c_str(),
+ cur_thread_name.c_str());
+}
+
+static void JNICALL ClassLoadCallback(jvmtiEnv* jenv,
+ JNIEnv* jni_env,
+ jthread thread,
+ jclass klass) {
+ std::string name = GetClassName(jenv, jni_env, klass);
+ if (name == "") {
+ return;
+ }
+ std::string thread_name = GetThreadName(jenv, jni_env, thread);
+ if (thread_name == "") {
+ return;
+ }
+ printf("Load: %s on %s\n", name.c_str(), thread_name.c_str());
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_enableClassLoadEvents(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean b) {
+ if (b == JNI_FALSE) {
+ jvmtiError ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_CLASS_LOAD,
+ nullptr);
+ if (JvmtiErrorToException(env, ret)) {
+ return;
+ }
+ ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_CLASS_PREPARE,
+ nullptr);
+ JvmtiErrorToException(env, ret);
+ return;
+ }
+
+ jvmtiEventCallbacks callbacks;
+ memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
+ callbacks.ClassLoad = ClassLoadCallback;
+ callbacks.ClassPrepare = ClassPrepareCallback;
+ jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
+ if (JvmtiErrorToException(env, ret)) {
+ return;
+ }
+
+ ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_CLASS_LOAD,
+ nullptr);
+ if (JvmtiErrorToException(env, ret)) {
+ return;
+ }
+ ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_CLASS_PREPARE,
+ nullptr);
+ JvmtiErrorToException(env, ret);
+}
+
} // namespace Test912Classes
} // namespace art
diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt
index a95a465..d0b77a4 100644
--- a/test/912-classes/expected.txt
+++ b/test/912-classes/expected.txt
@@ -59,3 +59,35 @@
boot <- src+src-ex (A,B)
912-classes.jar+ ->
[class A, class B, class java.lang.Object]
+
+[37, 0]
+
+B, false
+Load: LB; on main
+Prepare: LB; on main (cur=main)
+B, true
+Load: LB; on main
+Prepare: LB; on main (cur=main)
+C, false
+Load: LA; on main
+Prepare: LA; on main (cur=main)
+Load: LC; on main
+Prepare: LC; on main (cur=main)
+A, false
+C, true
+Load: LA; on main
+Prepare: LA; on main (cur=main)
+Load: LC; on main
+Prepare: LC; on main (cur=main)
+A, true
+A, true
+Load: LA; on main
+Prepare: LA; on main (cur=main)
+C, true
+Load: LC; on main
+Prepare: LC; on main (cur=main)
+C, true
+Load: LA; on TestRunner
+Prepare: LA; on TestRunner (cur=TestRunner)
+Load: LC; on TestRunner
+Prepare: LC; on TestRunner (cur=TestRunner)
diff --git a/test/912-classes/src-ex/A.java b/test/912-classes/src-ex/A.java
index 64acb2f..2c43cfb 100644
--- a/test/912-classes/src-ex/A.java
+++ b/test/912-classes/src-ex/A.java
@@ -15,4 +15,4 @@
*/
public class A {
-}
\ No newline at end of file
+}
diff --git a/test/912-classes/src-ex/C.java b/test/912-classes/src-ex/C.java
new file mode 100644
index 0000000..97f8021
--- /dev/null
+++ b/test/912-classes/src-ex/C.java
@@ -0,0 +1,18 @@
+/*
+ * 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 C extends A {
+}
diff --git a/test/912-classes/src/B.java b/test/912-classes/src/B.java
index f1458c3..52ce4dd 100644
--- a/test/912-classes/src/B.java
+++ b/test/912-classes/src/B.java
@@ -15,4 +15,4 @@
*/
public class B {
-}
\ No newline at end of file
+}
diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java
index ea3c49c..62dc9f9 100644
--- a/test/912-classes/src/Main.java
+++ b/test/912-classes/src/Main.java
@@ -21,8 +21,6 @@
public class Main {
public static void main(String[] args) throws Exception {
- System.loadLibrary(args[1]);
-
doTest();
}
@@ -80,6 +78,14 @@
testClassLoader(getProxyClass());
testClassLoaderClasses();
+
+ System.out.println();
+
+ testClassVersion();
+
+ System.out.println();
+
+ testClassEvents();
}
private static Class<?> proxyClass = null;
@@ -202,6 +208,65 @@
}
}
+ private static void testClassVersion() {
+ System.out.println(Arrays.toString(getClassVersion(Main.class)));
+ }
+
+ private static void testClassEvents() throws Exception {
+ ClassLoader cl = Main.class.getClassLoader();
+ while (cl.getParent() != null) {
+ cl = cl.getParent();
+ }
+ final ClassLoader boot = cl;
+
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ ClassLoader cl6 = create(boot, DEX1, DEX2);
+ System.out.println("C, true");
+ Class.forName("C", true, cl6);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+
+ enableClassLoadEvents(true);
+
+ ClassLoader cl1 = create(boot, DEX1, DEX2);
+ System.out.println("B, false");
+ Class.forName("B", false, cl1);
+
+ ClassLoader cl2 = create(boot, DEX1, DEX2);
+ System.out.println("B, true");
+ Class.forName("B", true, cl2);
+
+ ClassLoader cl3 = create(boot, DEX1, DEX2);
+ System.out.println("C, false");
+ Class.forName("C", false, cl3);
+ System.out.println("A, false");
+ Class.forName("A", false, cl3);
+
+ ClassLoader cl4 = create(boot, DEX1, DEX2);
+ System.out.println("C, true");
+ Class.forName("C", true, cl4);
+ System.out.println("A, true");
+ Class.forName("A", true, cl4);
+
+ ClassLoader cl5 = create(boot, DEX1, DEX2);
+ System.out.println("A, true");
+ Class.forName("A", true, cl5);
+ System.out.println("C, true");
+ Class.forName("C", true, cl5);
+
+ Thread t = new Thread(r, "TestRunner");
+ t.start();
+ t.join();
+
+ enableClassLoadEvents(false);
+ }
+
private static void printClassLoaderClasses(ClassLoader cl) {
for (;;) {
if (cl == null || !cl.getClass().getName().startsWith("dalvik.system")) {
@@ -262,6 +327,10 @@
private static native Class<?>[] getClassLoaderClasses(ClassLoader cl);
+ private static native int[] getClassVersion(Class<?> c);
+
+ private static native void enableClassLoadEvents(boolean b);
+
private static class TestForNonInit {
public static double dummy = Math.random(); // So it can't be compile-time initialized.
}
diff --git a/test/913-heaps/src/Main.java b/test/913-heaps/src/Main.java
index 564596e..5a11a5b 100644
--- a/test/913-heaps/src/Main.java
+++ b/test/913-heaps/src/Main.java
@@ -21,8 +21,6 @@
public class Main {
public static void main(String[] args) throws Exception {
- System.loadLibrary(args[1]);
-
doTest();
doFollowReferencesTest();
}
diff --git a/test/914-hello-obsolescence/src/Main.java b/test/914-hello-obsolescence/src/Main.java
index 46266ef..8a14716 100644
--- a/test/914-hello-obsolescence/src/Main.java
+++ b/test/914-hello-obsolescence/src/Main.java
@@ -53,7 +53,6 @@
"AAACIAAAEQAAAKIBAAADIAAAAgAAAJECAAAAIAAAAQAAAJ8CAAAAEAAAAQAAALACAAA=");
public static void main(String[] args) {
- System.loadLibrary(args[1]);
doTest(new Transform());
}
diff --git a/test/915-obsolete-2/src/Main.java b/test/915-obsolete-2/src/Main.java
index bbeb726..0e3145c 100644
--- a/test/915-obsolete-2/src/Main.java
+++ b/test/915-obsolete-2/src/Main.java
@@ -79,7 +79,6 @@
"IAAAFwAAAD4CAAADIAAABAAAAAgEAAAAIAAAAQAAACYEAAAAEAAAAQAAADwEAAA=");
public static void main(String[] args) {
- System.loadLibrary(args[1]);
doTest(new Transform());
}
diff --git a/test/916-obsolete-jit/src/Main.java b/test/916-obsolete-jit/src/Main.java
index 1e43f7e..1b03200 100644
--- a/test/916-obsolete-jit/src/Main.java
+++ b/test/916-obsolete-jit/src/Main.java
@@ -113,7 +113,6 @@
}
public static void main(String[] args) {
- System.loadLibrary(args[1]);
doTest(new Transform(), new TestWatcher());
}
diff --git a/test/917-fields-transformation/src/Main.java b/test/917-fields-transformation/src/Main.java
index 5378bb7..632a5c8 100644
--- a/test/917-fields-transformation/src/Main.java
+++ b/test/917-fields-transformation/src/Main.java
@@ -55,7 +55,6 @@
"AAIgAAAMAAAAXAEAAAMgAAACAAAA4QEAAAAgAAABAAAA8AEAAAAQAAABAAAABAIAAA==");
public static void main(String[] args) {
- System.loadLibrary(args[1]);
doTest(new Transform("Hello", "Goodbye"),
new Transform("start", "end"));
}
diff --git a/test/918-fields/src/Main.java b/test/918-fields/src/Main.java
index 8af6e7b..3ba535b 100644
--- a/test/918-fields/src/Main.java
+++ b/test/918-fields/src/Main.java
@@ -19,8 +19,6 @@
public class Main {
public static void main(String[] args) throws Exception {
- System.loadLibrary(args[1]);
-
doTest();
}
diff --git a/test/919-obsolete-fields/src/Main.java b/test/919-obsolete-fields/src/Main.java
index 895c7a3..1d893f1 100644
--- a/test/919-obsolete-fields/src/Main.java
+++ b/test/919-obsolete-fields/src/Main.java
@@ -116,7 +116,6 @@
}
public static void main(String[] args) {
- System.loadLibrary(args[1]);
TestWatcher w = new TestWatcher();
doTest(new Transform(w), w);
}
diff --git a/test/921-hello-failure/expected.txt b/test/921-hello-failure/expected.txt
index 1c1d4d9..9615e6b 100644
--- a/test/921-hello-failure/expected.txt
+++ b/test/921-hello-failure/expected.txt
@@ -21,3 +21,11 @@
Transformation error : java.lang.Exception(Failed to redefine classes <LTransform;, LTransform2;> due to JVMTI_ERROR_NAMES_DONT_MATCH)
hello - MultiRedef
hello2 - MultiRedef
+hello - MultiRetrans
+hello2 - MultiRetrans
+Transformation error : java.lang.Exception(Failed to retransform classes <LTransform2;, LTransform;> due to JVMTI_ERROR_NAMES_DONT_MATCH)
+hello - MultiRetrans
+hello2 - MultiRetrans
+Transformation error : java.lang.Exception(Failed to retransform classes <LTransform;, LTransform2;> due to JVMTI_ERROR_NAMES_DONT_MATCH)
+hello - MultiRetrans
+hello2 - MultiRetrans
diff --git a/test/921-hello-failure/src/Main.java b/test/921-hello-failure/src/Main.java
index 1fe2599..67ca1e1 100644
--- a/test/921-hello-failure/src/Main.java
+++ b/test/921-hello-failure/src/Main.java
@@ -18,13 +18,13 @@
public class Main {
public static void main(String[] args) {
- System.loadLibrary(args[1]);
NewName.doTest(new Transform());
DifferentAccess.doTest(new Transform());
NewInterface.doTest(new Transform2());
MissingInterface.doTest(new Transform2());
ReorderInterface.doTest(new Transform2());
MultiRedef.doTest(new Transform(), new Transform2());
+ MultiRetrans.doTest(new Transform(), new Transform2());
}
// Transforms the class. This throws an exception if something goes wrong.
@@ -47,7 +47,20 @@
dex_files.toArray(new byte[0][]));
}
+ public static void addMultiTransformationResults(CommonClassDefinition... defs) throws Exception {
+ for (CommonClassDefinition d : defs) {
+ addCommonTransformationResult(d.target.getCanonicalName(),
+ d.class_file_bytes,
+ d.dex_file_bytes);
+ }
+ }
+
public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
byte[][] classfiles,
byte[][] dexfiles) throws Exception;
+ public static native void doCommonClassRetransformation(Class<?>... target) throws Exception;
+ public static native void enableCommonRetransformation(boolean enable);
+ public static native void addCommonTransformationResult(String target_name,
+ byte[] class_bytes,
+ byte[] dex_bytes);
}
diff --git a/test/921-hello-failure/src/MultiRetrans.java b/test/921-hello-failure/src/MultiRetrans.java
new file mode 100644
index 0000000..95aaf07
--- /dev/null
+++ b/test/921-hello-failure/src/MultiRetrans.java
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+import java.util.Base64;
+
+class MultiRetrans {
+
+ // class NotTransform {
+ // public void sayHi(String name) {
+ // throw new Error("Should not be called!");
+ // }
+ // }
+ private static CommonClassDefinition INVALID_DEFINITION_T1 = new CommonClassDefinition(
+ Transform.class,
+ Base64.getDecoder().decode(
+ "yv66vgAAADQAFQoABgAPBwAQCAARCgACABIHABMHABQBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAP" +
+ "TGluZU51bWJlclRhYmxlAQAFc2F5SGkBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAApTb3VyY2VG" +
+ "aWxlAQARTm90VHJhbnNmb3JtLmphdmEMAAcACAEAD2phdmEvbGFuZy9FcnJvcgEAFVNob3VsZCBu" +
+ "b3QgYmUgY2FsbGVkIQwABwAMAQAMTm90VHJhbnNmb3JtAQAQamF2YS9sYW5nL09iamVjdAAgAAUA" +
+ "BgAAAAAAAgAAAAcACAABAAkAAAAdAAEAAQAAAAUqtwABsQAAAAEACgAAAAYAAQAAAAEAAQALAAwA" +
+ "AQAJAAAAIgADAAIAAAAKuwACWRIDtwAEvwAAAAEACgAAAAYAAQAAAAMAAQANAAAAAgAO"),
+ Base64.getDecoder().decode(
+ "ZGV4CjAzNQDLV95i5xnv6iUi6uIeDoY5jP5Xe9NP1AiYAgAAcAAAAHhWNBIAAAAAAAAAAAQCAAAL" +
+ "AAAAcAAAAAUAAACcAAAAAgAAALAAAAAAAAAAAAAAAAQAAADIAAAAAQAAAOgAAACQAQAACAEAAEoB" +
+ "AABSAQAAYgEAAHUBAACJAQAAnQEAALABAADHAQAAygEAAM4BAADiAQAAAQAAAAIAAAADAAAABAAA" +
+ "AAcAAAAHAAAABAAAAAAAAAAIAAAABAAAAEQBAAAAAAAAAAAAAAAAAQAKAAAAAQABAAAAAAACAAAA" +
+ "AAAAAAAAAAAAAAAAAgAAAAAAAAAFAAAAAAAAAPQBAAAAAAAAAQABAAEAAADpAQAABAAAAHAQAwAA" +
+ "AA4ABAACAAIAAADuAQAACQAAACIAAQAbAQYAAABwIAIAEAAnAAAAAQAAAAMABjxpbml0PgAOTE5v" +
+ "dFRyYW5zZm9ybTsAEUxqYXZhL2xhbmcvRXJyb3I7ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZh" +
+ "L2xhbmcvU3RyaW5nOwARTm90VHJhbnNmb3JtLmphdmEAFVNob3VsZCBub3QgYmUgY2FsbGVkIQAB" +
+ "VgACVkwAEmVtaXR0ZXI6IGphY2stNC4yMAAFc2F5SGkAAQAHDgADAQAHDgAAAAEBAICABIgCAQGg" +
+ "AgAADAAAAAAAAAABAAAAAAAAAAEAAAALAAAAcAAAAAIAAAAFAAAAnAAAAAMAAAACAAAAsAAAAAUA" +
+ "AAAEAAAAyAAAAAYAAAABAAAA6AAAAAEgAAACAAAACAEAAAEQAAABAAAARAEAAAIgAAALAAAASgEA" +
+ "AAMgAAACAAAA6QEAAAAgAAABAAAA9AEAAAAQAAABAAAABAIAAA=="));
+
+ // Valid redefinition of Transform2
+ // class Transform2 implements Iface1, Iface2 {
+ // public void sayHi(String name) {
+ // throw new Error("Should not be called!");
+ // }
+ // }
+ private static CommonClassDefinition VALID_DEFINITION_T2 = new CommonClassDefinition(
+ Transform2.class,
+ Base64.getDecoder().decode(
+ "yv66vgAAADQAGQoABgARBwASCAATCgACABQHABUHABYHABcHABgBAAY8aW5pdD4BAAMoKVYBAARD" +
+ "b2RlAQAPTGluZU51bWJlclRhYmxlAQAFc2F5SGkBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAApT" +
+ "b3VyY2VGaWxlAQAPVHJhbnNmb3JtMi5qYXZhDAAJAAoBAA9qYXZhL2xhbmcvRXJyb3IBABVTaG91" +
+ "bGQgbm90IGJlIGNhbGxlZCEMAAkADgEAClRyYW5zZm9ybTIBABBqYXZhL2xhbmcvT2JqZWN0AQAG" +
+ "SWZhY2UxAQAGSWZhY2UyACAABQAGAAIABwAIAAAAAgAAAAkACgABAAsAAAAdAAEAAQAAAAUqtwAB" +
+ "sQAAAAEADAAAAAYAAQAAAAEAAQANAA4AAQALAAAAIgADAAIAAAAKuwACWRIDtwAEvwAAAAEADAAA" +
+ "AAYAAQAAAAMAAQAPAAAAAgAQ"),
+ Base64.getDecoder().decode(
+ "ZGV4CjAzNQDSWls05CPkX+gbTGMVRvx9dc9vozzVbu7AAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAN" +
+ "AAAAcAAAAAcAAACkAAAAAgAAAMAAAAAAAAAAAAAAAAQAAADYAAAAAQAAAPgAAACoAQAAGAEAAGIB" +
+ "AABqAQAAdAEAAH4BAACMAQAAnwEAALMBAADHAQAA3gEAAO8BAADyAQAA9gEAAAoCAAABAAAAAgAA" +
+ "AAMAAAAEAAAABQAAAAYAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAABcAQAAAgAAAAAAAAACAAEA" +
+ "DAAAAAMAAQAAAAAABAAAAAAAAAACAAAAAAAAAAQAAABUAQAACAAAAAAAAAAcAgAAAAAAAAEAAQAB" +
+ "AAAAEQIAAAQAAABwEAMAAAAOAAQAAgACAAAAFgIAAAkAAAAiAAMAGwEHAAAAcCACABAAJwAAAAIA" +
+ "AAAAAAEAAQAAAAUABjxpbml0PgAITElmYWNlMTsACExJZmFjZTI7AAxMVHJhbnNmb3JtMjsAEUxq" +
+ "YXZhL2xhbmcvRXJyb3I7ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAV" +
+ "U2hvdWxkIG5vdCBiZSBjYWxsZWQhAA9UcmFuc2Zvcm0yLmphdmEAAVYAAlZMABJlbWl0dGVyOiBq" +
+ "YWNrLTQuMjAABXNheUhpAAEABw4AAwEABw4AAAABAQCAgASYAgEBsAIAAAwAAAAAAAAAAQAAAAAA" +
+ "AAABAAAADQAAAHAAAAACAAAABwAAAKQAAAADAAAAAgAAAMAAAAAFAAAABAAAANgAAAAGAAAAAQAA" +
+ "APgAAAABIAAAAgAAABgBAAABEAAAAgAAAFQBAAACIAAADQAAAGIBAAADIAAAAgAAABECAAAAIAAA" +
+ "AQAAABwCAAAAEAAAAQAAACwCAAA="));
+
+ public static void doTest(Transform t1, Transform2 t2) {
+ t1.sayHi("MultiRetrans");
+ t2.sayHi("MultiRetrans");
+ try {
+ Main.addMultiTransformationResults(VALID_DEFINITION_T2, INVALID_DEFINITION_T1);
+ Main.enableCommonRetransformation(true);
+ Main.doCommonClassRetransformation(Transform2.class, Transform.class);
+ } catch (Exception e) {
+ System.out.println(
+ "Transformation error : " + e.getClass().getName() + "(" + e.getMessage() + ")");
+ } finally {
+ Main.enableCommonRetransformation(false);
+ }
+ t1.sayHi("MultiRetrans");
+ t2.sayHi("MultiRetrans");
+ try {
+ Main.addMultiTransformationResults(VALID_DEFINITION_T2, INVALID_DEFINITION_T1);
+ Main.enableCommonRetransformation(true);
+ Main.doCommonClassRetransformation(Transform.class, Transform2.class);
+ } catch (Exception e) {
+ System.out.println(
+ "Transformation error : " + e.getClass().getName() + "(" + e.getMessage() + ")");
+ } finally {
+ Main.enableCommonRetransformation(false);
+ }
+ t1.sayHi("MultiRetrans");
+ t2.sayHi("MultiRetrans");
+ }
+}
diff --git a/test/922-properties/src/Main.java b/test/922-properties/src/Main.java
index 6cec6e9..8ad742f 100644
--- a/test/922-properties/src/Main.java
+++ b/test/922-properties/src/Main.java
@@ -19,8 +19,6 @@
public class Main {
public static void main(String[] args) throws Exception {
- System.loadLibrary(args[1]);
-
doTest();
}
diff --git a/test/923-monitors/src/Main.java b/test/923-monitors/src/Main.java
index e35ce12..ef00728 100644
--- a/test/923-monitors/src/Main.java
+++ b/test/923-monitors/src/Main.java
@@ -21,8 +21,6 @@
public class Main {
public static void main(String[] args) throws Exception {
- System.loadLibrary(args[1]);
-
doTest();
}
diff --git a/test/924-threads/expected.txt b/test/924-threads/expected.txt
index 32e3368..67d20eb 100644
--- a/test/924-threads/expected.txt
+++ b/test/924-threads/expected.txt
@@ -29,3 +29,9 @@
5 = ALIVE|RUNNABLE
2 = TERMINATED
[Thread[FinalizerDaemon,5,system], Thread[FinalizerWatchdogDaemon,5,system], Thread[HeapTaskDaemon,5,system], Thread[ReferenceQueueDaemon,5,system], Thread[Signal Catcher,5,system], Thread[main,5,main]]
+JVMTI_ERROR_THREAD_NOT_ALIVE
+JVMTI_ERROR_THREAD_NOT_ALIVE
+Constructed thread
+Thread(EventTestThread): start
+Thread(EventTestThread): end
+Thread joined
diff --git a/test/924-threads/src/Main.java b/test/924-threads/src/Main.java
index 492a7ac..29c4aa3 100644
--- a/test/924-threads/src/Main.java
+++ b/test/924-threads/src/Main.java
@@ -25,8 +25,6 @@
public class Main {
public static void main(String[] args) throws Exception {
- System.loadLibrary(args[1]);
-
doTest();
}
@@ -56,6 +54,10 @@
doStateTests();
doAllThreadsTests();
+
+ doTLSTests();
+
+ doTestEvents();
}
private static class Holder {
@@ -164,6 +166,84 @@
System.out.println(Arrays.toString(threads));
}
+ private static void doTLSTests() throws Exception {
+ doTLSNonLiveTests();
+ doTLSLiveTests();
+ }
+
+ private static void doTLSNonLiveTests() throws Exception {
+ Thread t = new Thread();
+ try {
+ setTLS(t, 1);
+ System.out.println("Expected failure setting TLS for non-live thread");
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+ t.start();
+ t.join();
+ try {
+ setTLS(t, 1);
+ System.out.println("Expected failure setting TLS for non-live thread");
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ private static void doTLSLiveTests() throws Exception {
+ setTLS(Thread.currentThread(), 1);
+
+ long l = getTLS(Thread.currentThread());
+ if (l != 1) {
+ throw new RuntimeException("Unexpected TLS value: " + l);
+ };
+
+ final CountDownLatch cdl1 = new CountDownLatch(1);
+ final CountDownLatch cdl2 = new CountDownLatch(1);
+
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ cdl1.countDown();
+ cdl2.await();
+ setTLS(Thread.currentThread(), 2);
+ if (getTLS(Thread.currentThread()) != 2) {
+ throw new RuntimeException("Different thread issue");
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+
+ Thread t = new Thread(r);
+ t.start();
+ cdl1.await();
+ setTLS(Thread.currentThread(), 1);
+ cdl2.countDown();
+
+ t.join();
+ if (getTLS(Thread.currentThread()) != 1) {
+ throw new RuntimeException("Got clobbered");
+ }
+ }
+
+ private static void doTestEvents() throws Exception {
+ enableThreadEvents(true);
+
+ Thread t = new Thread("EventTestThread");
+
+ System.out.println("Constructed thread");
+ Thread.yield();
+
+ t.start();
+ t.join();
+
+ System.out.println("Thread joined");
+
+ enableThreadEvents(false);
+ }
+
private final static Comparator<Thread> THREAD_COMP = new Comparator<Thread>() {
public int compare(Thread o1, Thread o2) {
return o1.getName().compareTo(o2.getName());
@@ -229,4 +309,7 @@
private static native Object[] getThreadInfo(Thread t);
private static native int getThreadState(Thread t);
private static native Thread[] getAllThreads();
+ private static native void setTLS(Thread t, long l);
+ private static native long getTLS(Thread t);
+ private static native void enableThreadEvents(boolean b);
}
diff --git a/test/924-threads/threads.cc b/test/924-threads/threads.cc
index 1487b7c..0380433 100644
--- a/test/924-threads/threads.cc
+++ b/test/924-threads/threads.cc
@@ -120,5 +120,88 @@
return ret;
}
+extern "C" JNIEXPORT jlong JNICALL Java_Main_getTLS(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread) {
+ void* tls;
+ jvmtiError result = jvmti_env->GetThreadLocalStorage(thread, &tls);
+ if (JvmtiErrorToException(env, result)) {
+ return 0;
+ }
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(tls));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_setTLS(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread, jlong val) {
+ const void* tls = reinterpret_cast<void*>(static_cast<uintptr_t>(val));
+ jvmtiError result = jvmti_env->SetThreadLocalStorage(thread, tls);
+ JvmtiErrorToException(env, result);
+}
+
+static void JNICALL ThreadEvent(jvmtiEnv* jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ bool is_start) {
+ jvmtiThreadInfo info;
+ jvmtiError result = jvmti_env->GetThreadInfo(thread, &info);
+ if (result != JVMTI_ERROR_NONE) {
+ printf("Error getting thread info");
+ return;
+ }
+ printf("Thread(%s): %s\n", info.name, is_start ? "start" : "end");
+
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(info.name));
+ jni_env->DeleteLocalRef(info.thread_group);
+ jni_env->DeleteLocalRef(info.context_class_loader);
+}
+
+static void JNICALL ThreadStart(jvmtiEnv* jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread) {
+ ThreadEvent(jvmti_env, jni_env, thread, true);
+}
+
+static void JNICALL ThreadEnd(jvmtiEnv* jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread) {
+ ThreadEvent(jvmti_env, jni_env, thread, false);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_enableThreadEvents(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean b) {
+ if (b == JNI_FALSE) {
+ jvmtiError ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_THREAD_START,
+ nullptr);
+ if (JvmtiErrorToException(env, ret)) {
+ return;
+ }
+ ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_THREAD_END,
+ nullptr);
+ JvmtiErrorToException(env, ret);
+ return;
+ }
+
+ jvmtiEventCallbacks callbacks;
+ memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
+ callbacks.ThreadStart = ThreadStart;
+ callbacks.ThreadEnd = ThreadEnd;
+ jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
+ if (JvmtiErrorToException(env, ret)) {
+ return;
+ }
+
+ ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_THREAD_START,
+ nullptr);
+ if (JvmtiErrorToException(env, ret)) {
+ return;
+ }
+ ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_THREAD_END,
+ nullptr);
+ JvmtiErrorToException(env, ret);
+}
+
} // namespace Test924Threads
} // namespace art
diff --git a/test/925-threadgroups/src/Main.java b/test/925-threadgroups/src/Main.java
index c59efe2..3d7a4ca 100644
--- a/test/925-threadgroups/src/Main.java
+++ b/test/925-threadgroups/src/Main.java
@@ -19,8 +19,6 @@
public class Main {
public static void main(String[] args) throws Exception {
- System.loadLibrary(args[1]);
-
doTest();
}
diff --git a/test/926-multi-obsolescence/src/Main.java b/test/926-multi-obsolescence/src/Main.java
index 8a6cf84..6d9f96c 100644
--- a/test/926-multi-obsolescence/src/Main.java
+++ b/test/926-multi-obsolescence/src/Main.java
@@ -92,7 +92,6 @@
"AACUAQAAAiAAABEAAACiAQAAAyAAAAIAAACXAgAAACAAAAEAAAClAgAAABAAAAEAAAC0AgAA"));
public static void main(String[] args) {
- System.loadLibrary(args[1]);
doTest(new Transform(), new Transform2());
}
diff --git a/test/927-timers/src/Main.java b/test/927-timers/src/Main.java
index 2f5c85c..b67f66d 100644
--- a/test/927-timers/src/Main.java
+++ b/test/927-timers/src/Main.java
@@ -18,8 +18,6 @@
public class Main {
public static void main(String[] args) throws Exception {
- System.loadLibrary(args[1]);
-
doTest();
}
diff --git a/test/928-jni-table/src/Main.java b/test/928-jni-table/src/Main.java
index b0baea1..fd61b7d 100644
--- a/test/928-jni-table/src/Main.java
+++ b/test/928-jni-table/src/Main.java
@@ -16,8 +16,6 @@
public class Main {
public static void main(String[] args) throws Exception {
- System.loadLibrary(args[1]);
-
doJNITableTest();
System.out.println("Done");
diff --git a/test/929-search/src/Main.java b/test/929-search/src/Main.java
index d253e6f..bbeb081 100644
--- a/test/929-search/src/Main.java
+++ b/test/929-search/src/Main.java
@@ -18,8 +18,6 @@
public class Main {
public static void main(String[] args) throws Exception {
- System.loadLibrary(args[1]);
-
doTest();
}
diff --git a/test/930-hello-retransform/build b/test/930-hello-retransform/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/930-hello-retransform/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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-build "$@" --experimental agents
diff --git a/test/930-hello-retransform/expected.txt b/test/930-hello-retransform/expected.txt
new file mode 100644
index 0000000..4774b81
--- /dev/null
+++ b/test/930-hello-retransform/expected.txt
@@ -0,0 +1,2 @@
+hello
+Goodbye
diff --git a/test/930-hello-retransform/info.txt b/test/930-hello-retransform/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/930-hello-retransform/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/930-hello-retransform/run b/test/930-hello-retransform/run
new file mode 100755
index 0000000..4379349
--- /dev/null
+++ b/test/930-hello-retransform/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 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 "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --jvmti
diff --git a/test/930-hello-retransform/src/Main.java b/test/930-hello-retransform/src/Main.java
new file mode 100644
index 0000000..0063c82
--- /dev/null
+++ b/test/930-hello-retransform/src/Main.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+public class Main {
+
+ /**
+ * base64 encoded class/dex file for
+ * class Transform {
+ * public void sayHi() {
+ * System.out.println("Goodbye");
+ * }
+ * }
+ */
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
+ "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" +
+ "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq" +
+ "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph" +
+ "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABQAG" +
+ "AAAAAAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB" +
+ "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAATAAgAFAABAAwAAAACAA0=");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
+ "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
+ "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" +
+ "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
+ "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" +
+ "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" +
+ "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" +
+ "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" +
+ "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" +
+ "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" +
+ "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
+ "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
+ "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=");
+
+ public static void main(String[] args) {
+ doTest(new Transform());
+ }
+
+ public static void doTest(Transform t) {
+ t.sayHi();
+ addCommonTransformationResult("Transform", CLASS_BYTES, DEX_BYTES);
+ enableCommonRetransformation(true);
+ doCommonClassRetransformation(Transform.class);
+ t.sayHi();
+ }
+
+ // Transforms the class
+ private static native void doCommonClassRetransformation(Class<?>... target);
+ private static native void enableCommonRetransformation(boolean enable);
+ private static native void addCommonTransformationResult(String target_name,
+ byte[] class_bytes,
+ byte[] dex_bytes);
+}
diff --git a/test/930-hello-retransform/src/Transform.java b/test/930-hello-retransform/src/Transform.java
new file mode 100644
index 0000000..8e8af35
--- /dev/null
+++ b/test/930-hello-retransform/src/Transform.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 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 Transform {
+ public void sayHi() {
+ // Use lower 'h' to make sure the string will have a different string id
+ // than the transformation (the transformation code is the same except
+ // the actual printed String, which was making the test inacurately passing
+ // in JIT mode when loading the string from the dex cache, as the string ids
+ // of the two different strings were the same).
+ // We know the string ids will be different because lexicographically:
+ // "Goodbye" < "LTransform;" < "hello".
+ System.out.println("hello");
+ }
+}
diff --git a/test/931-agent-thread/agent_thread.cc b/test/931-agent-thread/agent_thread.cc
new file mode 100644
index 0000000..6ace4ce
--- /dev/null
+++ b/test/931-agent-thread/agent_thread.cc
@@ -0,0 +1,132 @@
+/*
+ * 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 <inttypes.h>
+
+#include "barrier.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "runtime.h"
+#include "ScopedLocalRef.h"
+#include "thread-inl.h"
+#include "well_known_classes.h"
+
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test930AgentThread {
+
+struct AgentData {
+ AgentData() : main_thread(nullptr),
+ jvmti_env(nullptr),
+ b(2) {
+ }
+
+ jthread main_thread;
+ jvmtiEnv* jvmti_env;
+ Barrier b;
+ jint priority;
+};
+
+static void AgentMain(jvmtiEnv* jenv, JNIEnv* env, void* arg) {
+ AgentData* data = reinterpret_cast<AgentData*>(arg);
+
+ // Check some basics.
+ // This thread is not the main thread.
+ jthread this_thread;
+ jvmtiError this_thread_result = jenv->GetCurrentThread(&this_thread);
+ CHECK(!JvmtiErrorToException(env, this_thread_result));
+ CHECK(!env->IsSameObject(this_thread, data->main_thread));
+
+ // The thread is a daemon.
+ jvmtiThreadInfo info;
+ jvmtiError info_result = jenv->GetThreadInfo(this_thread, &info);
+ CHECK(!JvmtiErrorToException(env, info_result));
+ CHECK(info.is_daemon);
+
+ // The thread has the requested priority.
+ // TODO: Our thread priorities do not work on the host.
+ // CHECK_EQ(info.priority, data->priority);
+
+ // Check further parts of the thread:
+ jint thread_count;
+ jthread* threads;
+ jvmtiError threads_result = jenv->GetAllThreads(&thread_count, &threads);
+ CHECK(!JvmtiErrorToException(env, threads_result));
+ bool found = false;
+ for (jint i = 0; i != thread_count; ++i) {
+ if (env->IsSameObject(threads[i], this_thread)) {
+ found = true;
+ break;
+ }
+ }
+ CHECK(found);
+
+ // Done, let the main thread progress.
+ data->b.Pass(Thread::Current());
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_testAgentThread(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
+ // Create a Thread object.
+ ScopedLocalRef<jobject> thread_name(env,
+ env->NewStringUTF("Agent Thread"));
+ if (thread_name.get() == nullptr) {
+ return;
+ }
+
+ ScopedLocalRef<jobject> thread(env, env->AllocObject(WellKnownClasses::java_lang_Thread));
+ if (thread.get() == nullptr) {
+ return;
+ }
+
+ env->CallNonvirtualVoidMethod(thread.get(),
+ WellKnownClasses::java_lang_Thread,
+ WellKnownClasses::java_lang_Thread_init,
+ Runtime::Current()->GetMainThreadGroup(),
+ thread_name.get(),
+ kMinThreadPriority,
+ JNI_FALSE);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+
+ jthread main_thread;
+ jvmtiError main_thread_result = jvmti_env->GetCurrentThread(&main_thread);
+ if (JvmtiErrorToException(env, main_thread_result)) {
+ return;
+ }
+
+ AgentData data;
+ data.main_thread = env->NewGlobalRef(main_thread);
+ data.jvmti_env = jvmti_env;
+ data.priority = JVMTI_THREAD_MIN_PRIORITY;
+
+ jvmtiError result = jvmti_env->RunAgentThread(thread.get(), AgentMain, &data, data.priority);
+ if (JvmtiErrorToException(env, result)) {
+ return;
+ }
+
+ data.b.Wait(Thread::Current());
+
+ env->DeleteGlobalRef(data.main_thread);
+}
+
+} // namespace Test930AgentThread
+} // namespace art
diff --git a/test/931-agent-thread/build b/test/931-agent-thread/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/931-agent-thread/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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-build "$@" --experimental agents
diff --git a/test/931-agent-thread/expected.txt b/test/931-agent-thread/expected.txt
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/test/931-agent-thread/expected.txt
@@ -0,0 +1 @@
+Done
diff --git a/test/931-agent-thread/info.txt b/test/931-agent-thread/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/931-agent-thread/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/931-agent-thread/run b/test/931-agent-thread/run
new file mode 100755
index 0000000..0a8d067
--- /dev/null
+++ b/test/931-agent-thread/run
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+# This test checks whether dex files can be injected into parent classloaders. App images preload
+# classes, which will make the injection moot. Turn off app images to avoid the issue.
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --jvmti \
+ --no-app-image
diff --git a/test/931-agent-thread/src/Main.java b/test/931-agent-thread/src/Main.java
new file mode 100644
index 0000000..a7639fb
--- /dev/null
+++ b/test/931-agent-thread/src/Main.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+import java.util.Arrays;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ testAgentThread();
+
+ System.out.println("Done");
+ }
+
+ private static native void testAgentThread();
+}
diff --git a/test/932-transform-saves/build b/test/932-transform-saves/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/932-transform-saves/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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-build "$@" --experimental agents
diff --git a/test/932-transform-saves/expected.txt b/test/932-transform-saves/expected.txt
new file mode 100644
index 0000000..5097771
--- /dev/null
+++ b/test/932-transform-saves/expected.txt
@@ -0,0 +1,3 @@
+hello
+Goodbye
+hello
diff --git a/test/932-transform-saves/info.txt b/test/932-transform-saves/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/932-transform-saves/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/932-transform-saves/run b/test/932-transform-saves/run
new file mode 100755
index 0000000..4379349
--- /dev/null
+++ b/test/932-transform-saves/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 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 "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --jvmti
diff --git a/test/932-transform-saves/src/Main.java b/test/932-transform-saves/src/Main.java
new file mode 100644
index 0000000..d960322
--- /dev/null
+++ b/test/932-transform-saves/src/Main.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+public class Main {
+ /**
+ * base64 encoded class/dex file for
+ * class Transform {
+ * public void sayHi() {
+ * System.out.println("hello");
+ * }
+ * }
+ */
+ private static final byte[] CLASS_BYTES_A = Base64.getDecoder().decode(
+ "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
+ "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" +
+ "BwAIBwAWDAAXABgBAAVoZWxsbwcAGQwAGgAbAQAJVHJhbnNmb3JtAQAQamF2YS9sYW5nL09iamVj" +
+ "dAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZh" +
+ "L2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAgAAUABgAA" +
+ "AAAAAgAAAAcACAABAAkAAAAdAAEAAQAAAAUqtwABsQAAAAEACgAAAAYAAQAAABEAAQALAAgAAQAJ" +
+ "AAAAJQACAAEAAAAJsgACEgO2AASxAAAAAQAKAAAACgACAAAAGgAIABsAAQAMAAAAAgAN");
+ private static final byte[] DEX_BYTES_A = Base64.getDecoder().decode(
+ "ZGV4CjAzNQC6XWInnnDd1H4NdQ3P3inH8eCVmQI6W7LMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
+ "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
+ "AABqAQAAdwEAAI4BAACiAQAAtgEAAMoBAADaAQAA3QEAAOEBAAD1AQAA/AEAAAECAAAKAgAAAQAA" +
+ "AAIAAAADAAAABAAAAAUAAAAHAAAABwAAAAUAAAAAAAAACAAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
+ "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAGAAAAAAAAABwCAAAA" +
+ "AAAAAQABAAEAAAARAgAABAAAAHAQAwAAAA4AAwABAAIAAAAWAgAACQAAAGIAAAAbAQoAAABuIAIA" +
+ "EAAOAAAAAQAAAAMABjxpbml0PgALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwAS" +
+ "TGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVt" +
+ "OwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMjIABWhlbGxvAANvdXQA" +
+ "B3ByaW50bG4ABXNheUhpABEABw4AGgAHDocAAAABAQCAgASgAgEBuAIAAA0AAAAAAAAAAQAAAAAA" +
+ "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
+ "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
+ "AgAAABECAAAAIAAAAQAAABwCAAAAEAAAAQAAACwCAAA=");
+
+ /**
+ * base64 encoded class/dex file for
+ * class Transform {
+ * public void sayHi() {
+ * System.out.println("Goodbye");
+ * }
+ * }
+ */
+ private static final byte[] CLASS_BYTES_B = Base64.getDecoder().decode(
+ "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
+ "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" +
+ "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq" +
+ "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph" +
+ "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABQAG" +
+ "AAAAAAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB" +
+ "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAATAAgAFAABAAwAAAACAA0=");
+ private static final byte[] DEX_BYTES_B = Base64.getDecoder().decode(
+ "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
+ "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
+ "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" +
+ "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
+ "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" +
+ "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" +
+ "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" +
+ "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" +
+ "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" +
+ "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" +
+ "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
+ "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
+ "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=");
+
+ public static void main(String[] args) {
+ doTest(new Transform());
+ }
+
+ public static void doTest(Transform t) {
+ // TODO We currently need to do this transform call since we don't have any way to make the
+ // original-dex-file a single-class dex-file letting us restore it easily. We should use the
+ // manipulation library that is being made when we store the original dex file.
+ // TODO REMOVE this theoretically does nothing but it ensures the original-dex-file we have set
+ // is one we can return to unaltered.
+ doCommonClassRedefinition(Transform.class, CLASS_BYTES_A, DEX_BYTES_A);
+ t.sayHi();
+
+ // Now turn it into DEX_BYTES_B so it says 'Goodbye'
+ addCommonTransformationResult("Transform", CLASS_BYTES_B, DEX_BYTES_B);
+ enableCommonRetransformation(true);
+ doCommonClassRetransformation(Transform.class);
+ t.sayHi();
+
+ // Now turn it back to normal by removing the load-hook and transforming again.
+ enableCommonRetransformation(false);
+ doCommonClassRetransformation(Transform.class);
+ t.sayHi();
+ }
+
+ // Transforms the class
+ private static native void doCommonClassRedefinition(Class<?> target,
+ byte[] class_bytes,
+ byte[] dex_bytes);
+ private static native void doCommonClassRetransformation(Class<?>... target);
+ private static native void enableCommonRetransformation(boolean enable);
+ private static native void addCommonTransformationResult(String target_name,
+ byte[] class_bytes,
+ byte[] dex_bytes);
+}
diff --git a/test/932-transform-saves/src/Transform.java b/test/932-transform-saves/src/Transform.java
new file mode 100644
index 0000000..8e8af35
--- /dev/null
+++ b/test/932-transform-saves/src/Transform.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 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 Transform {
+ public void sayHi() {
+ // Use lower 'h' to make sure the string will have a different string id
+ // than the transformation (the transformation code is the same except
+ // the actual printed String, which was making the test inacurately passing
+ // in JIT mode when loading the string from the dex cache, as the string ids
+ // of the two different strings were the same).
+ // We know the string ids will be different because lexicographically:
+ // "Goodbye" < "LTransform;" < "hello".
+ System.out.println("hello");
+ }
+}
diff --git a/test/933-misc-events/build b/test/933-misc-events/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/933-misc-events/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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-build "$@" --experimental agents
diff --git a/test/933-misc-events/expected.txt b/test/933-misc-events/expected.txt
new file mode 100644
index 0000000..024c560
--- /dev/null
+++ b/test/933-misc-events/expected.txt
@@ -0,0 +1,2 @@
+Received dump request.
+Done
diff --git a/test/933-misc-events/info.txt b/test/933-misc-events/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/933-misc-events/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/933-misc-events/misc_events.cc b/test/933-misc-events/misc_events.cc
new file mode 100644
index 0000000..860d4b5
--- /dev/null
+++ b/test/933-misc-events/misc_events.cc
@@ -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.
+ */
+
+#include <atomic>
+#include <signal.h>
+#include <sys/types.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test933MiscEvents {
+
+static std::atomic<bool> saw_dump_request(false);
+
+static void DumpRequestCallback(jvmtiEnv* jenv ATTRIBUTE_UNUSED) {
+ printf("Received dump request.\n");
+ saw_dump_request.store(true, std::memory_order::memory_order_relaxed);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_testSigQuit(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
+ jvmtiEventCallbacks callbacks;
+ memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
+ callbacks.DataDumpRequest = DumpRequestCallback;
+ jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
+ if (JvmtiErrorToException(env, ret)) {
+ return;
+ }
+
+ ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_DATA_DUMP_REQUEST,
+ nullptr);
+ if (JvmtiErrorToException(env, ret)) {
+ return;
+ }
+
+ // Send sigquit to self.
+ kill(getpid(), SIGQUIT);
+
+ // Busy-wait for request.
+ for (;;) {
+ sleep(1);
+ if (saw_dump_request.load(std::memory_order::memory_order_relaxed)) {
+ break;
+ }
+ }
+
+ ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_DATA_DUMP_REQUEST, nullptr);
+ JvmtiErrorToException(env, ret);
+}
+
+} // namespace Test933MiscEvents
+} // namespace art
diff --git a/test/933-misc-events/run b/test/933-misc-events/run
new file mode 100755
index 0000000..0a8d067
--- /dev/null
+++ b/test/933-misc-events/run
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+# This test checks whether dex files can be injected into parent classloaders. App images preload
+# classes, which will make the injection moot. Turn off app images to avoid the issue.
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --jvmti \
+ --no-app-image
diff --git a/test/933-misc-events/src/Main.java b/test/933-misc-events/src/Main.java
new file mode 100644
index 0000000..89801a3
--- /dev/null
+++ b/test/933-misc-events/src/Main.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ testSigQuit();
+
+ System.out.println("Done");
+ }
+
+ private static native void testSigQuit();
+}
diff --git a/test/Android.bp b/test/Android.bp
index 965d07a..89e4092 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -269,6 +269,8 @@
"927-timers/timers.cc",
"928-jni-table/jni_table.cc",
"929-search/search.cc",
+ "931-agent-thread/agent_thread.cc",
+ "933-misc-events/misc_events.cc",
],
shared_libs: [
"libbase",
@@ -325,6 +327,7 @@
"570-checker-osr/osr.cc",
"595-profile-saving/profile-saving.cc",
"596-app-images/app_images.cc",
+ "596-monitor-inflation/monitor_inflation.cc",
"597-deopt-new-string/deopt.cc",
"626-const-class-linking/clear_dex_cache_types.cc",
],
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index e604c93..814f968 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -277,39 +277,6 @@
147-stripped-dex-fallback \
569-checker-pattern-replacement
-# These 9** tests are not supported in current form due to linker
-# restrictions. See b/31681198
-TEST_ART_BROKEN_TARGET_TESTS += \
- 901-hello-ti-agent \
- 902-hello-transformation \
- 903-hello-tagging \
- 904-object-allocation \
- 905-object-free \
- 906-iterate-heap \
- 907-get-loaded-classes \
- 908-gc-start-finish \
- 909-attach-agent \
- 910-methods \
- 911-get-stack-trace \
- 912-classes \
- 913-heaps \
- 914-hello-obsolescence \
- 915-obsolete-2 \
- 916-obsolete-jit \
- 917-fields-transformation \
- 918-fields \
- 919-obsolete-fields \
- 920-objects \
- 921-hello-failure \
- 922-properties \
- 923-monitors \
- 924-threads \
- 925-threadgroups \
- 926-multi-obsolescence \
- 927-timers \
- 928-jni-table \
- 929-search \
-
ifneq (,$(filter target,$(TARGET_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
$(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
diff --git a/test/XandY/Y.java b/test/XandY/Y.java
index ecead6e..2a1f036 100644
--- a/test/XandY/Y.java
+++ b/test/XandY/Y.java
@@ -14,4 +14,8 @@
* limitations under the License.
*/
-class Y extends X {}
+class Y extends X {
+ static Z z = new Z();
+ static class Z {
+ }
+}
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 5f1071f..28fa130 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -44,7 +44,7 @@
SECONDARY_DEX=""
TIME_OUT="gdb" # "n" (disabled), "timeout" (use timeout), "gdb" (use gdb)
# Value in seconds
-if [ "$ART_USE_READ_BARRIER" = "true" ]; then
+if [ "$ART_USE_READ_BARRIER" != "false" ]; then
TIME_OUT_VALUE=2400 # 40 minutes.
else
TIME_OUT_VALUE=1200 # 20 minutes.
diff --git a/test/run-test b/test/run-test
index a913e78..c78fa35 100755
--- a/test/run-test
+++ b/test/run-test
@@ -131,6 +131,7 @@
multi_image_suffix=""
android_root="/system"
bisection_search="no"
+suspend_timeout="500000"
# By default we will use optimizing.
image_args=""
image_suffix=""
@@ -219,6 +220,10 @@
basic_verify="true"
gc_stress="true"
shift
+ elif [ "x$1" = "x--suspend-timeout" ]; then
+ shift
+ suspend_timeout="$1"
+ shift
elif [ "x$1" = "x--image" ]; then
shift
image="$1"
@@ -402,6 +407,9 @@
tmp_dir="`cd $oldwd ; python -c "import os; print os.path.realpath('$tmp_dir')"`"
mkdir -p $tmp_dir
+# Add thread suspend timeout flag
+run_args="${run_args} --runtime-option -XX:ThreadSuspendTimeout=$suspend_timeout"
+
if [ "$basic_verify" = "true" ]; then
# Set HspaceCompactForOOMMinIntervalMs to zero to run hspace compaction for OOM more frequently in tests.
run_args="${run_args} --runtime-option -Xgc:preverify --runtime-option -Xgc:postverify --runtime-option -XX:HspaceCompactForOOMMinIntervalMs=0"
@@ -649,6 +657,7 @@
echo " --quiet Don't print anything except failure messages"
echo " --bisection-search Perform bisection bug search."
echo " --vdex Test using vdex as in input to dex2oat. Only works with --prebuild."
+ echo " --suspend-timeout Change thread suspend timeout ms (default 500000)."
) 1>&2 # Direct to stderr so usage is not printed if --quiet is set.
exit 1
fi
@@ -722,7 +731,7 @@
#
# TODO: Enable Checker when read barrier support is added to more
# architectures (b/12687968).
- if [ "x$ART_USE_READ_BARRIER" = xtrue ] \
+ if [ "x$ART_USE_READ_BARRIER" != xfalse ] \
&& (([ "x$host_mode" = "xyes" ] \
&& ! arch_supports_read_barrier "$host_arch_name") \
|| ([ "x$target_mode" = "xyes" ] \
diff --git a/test/ti-agent/common_helper.cc b/test/ti-agent/common_helper.cc
index 2c6d3ed..80e1797 100644
--- a/test/ti-agent/common_helper.cc
+++ b/test/ti-agent/common_helper.cc
@@ -16,13 +16,18 @@
#include "ti-agent/common_helper.h"
+#include <dlfcn.h>
#include <stdio.h>
#include <sstream>
+#include <deque>
+#include "android-base/stringprintf.h"
#include "art_method.h"
#include "jni.h"
+#include "jni_internal.h"
#include "openjdkjvmti/jvmti.h"
#include "scoped_thread_state_change-inl.h"
+#include "ScopedLocalRef.h"
#include "stack.h"
#include "ti-agent/common_load.h"
#include "utils.h"
@@ -60,17 +65,17 @@
return true;
}
-namespace common_redefine {
-static void throwRedefinitionError(jvmtiEnv* jvmti,
- JNIEnv* env,
- jint num_targets,
- jclass* target,
- jvmtiError res) {
+template <bool is_redefine>
+static void throwCommonRedefinitionError(jvmtiEnv* jvmti,
+ JNIEnv* env,
+ jint num_targets,
+ jclass* target,
+ jvmtiError res) {
std::stringstream err;
char* error = nullptr;
jvmti->GetErrorName(res, &error);
- err << "Failed to redefine class";
+ err << "Failed to " << (is_redefine ? "redefine" : "retransform") << " class";
if (num_targets > 1) {
err << "es";
}
@@ -92,6 +97,16 @@
env->ThrowNew(env->FindClass("java/lang/Exception"), message.c_str());
}
+namespace common_redefine {
+
+static void throwRedefinitionError(jvmtiEnv* jvmti,
+ JNIEnv* env,
+ jint num_targets,
+ jclass* target,
+ jvmtiError res) {
+ return throwCommonRedefinitionError<true>(jvmti, env, num_targets, target, res);
+}
+
static void DoMultiClassRedefine(jvmtiEnv* jvmti_env,
JNIEnv* env,
jint num_redefines,
@@ -161,7 +176,139 @@
dex_files.data());
}
-// Don't do anything
+// Get all capabilities except those related to retransformation.
+jint OnLoad(JavaVM* vm,
+ char* options ATTRIBUTE_UNUSED,
+ void* reserved ATTRIBUTE_UNUSED) {
+ if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+ printf("Unable to get jvmti env!\n");
+ return 1;
+ }
+ jvmtiCapabilities caps;
+ jvmti_env->GetPotentialCapabilities(&caps);
+ caps.can_retransform_classes = 0;
+ caps.can_retransform_any_class = 0;
+ jvmti_env->AddCapabilities(&caps);
+ return 0;
+}
+
+} // namespace common_redefine
+
+namespace common_retransform {
+
+struct CommonTransformationResult {
+ std::vector<unsigned char> class_bytes;
+ std::vector<unsigned char> dex_bytes;
+
+ CommonTransformationResult(size_t class_size, size_t dex_size)
+ : class_bytes(class_size), dex_bytes(dex_size) {}
+
+ CommonTransformationResult() = default;
+ CommonTransformationResult(CommonTransformationResult&&) = default;
+ CommonTransformationResult(CommonTransformationResult&) = default;
+};
+
+// Map from class name to transformation result.
+std::map<std::string, std::deque<CommonTransformationResult>> gTransformations;
+
+extern "C" JNIEXPORT void JNICALL Java_Main_addCommonTransformationResult(JNIEnv* env,
+ jclass,
+ jstring class_name,
+ jbyteArray class_array,
+ jbyteArray dex_array) {
+ const char* name_chrs = env->GetStringUTFChars(class_name, nullptr);
+ std::string name_str(name_chrs);
+ env->ReleaseStringUTFChars(class_name, name_chrs);
+ CommonTransformationResult trans(env->GetArrayLength(class_array),
+ env->GetArrayLength(dex_array));
+ if (env->ExceptionOccurred()) {
+ return;
+ }
+ env->GetByteArrayRegion(class_array,
+ 0,
+ env->GetArrayLength(class_array),
+ reinterpret_cast<jbyte*>(trans.class_bytes.data()));
+ if (env->ExceptionOccurred()) {
+ return;
+ }
+ env->GetByteArrayRegion(dex_array,
+ 0,
+ env->GetArrayLength(dex_array),
+ reinterpret_cast<jbyte*>(trans.dex_bytes.data()));
+ if (env->ExceptionOccurred()) {
+ return;
+ }
+ if (gTransformations.find(name_str) == gTransformations.end()) {
+ std::deque<CommonTransformationResult> list;
+ gTransformations[name_str] = std::move(list);
+ }
+ gTransformations[name_str].push_back(std::move(trans));
+}
+
+// The hook we are using.
+void JNICALL CommonClassFileLoadHookRetransformable(jvmtiEnv* jvmti_env,
+ JNIEnv* jni_env ATTRIBUTE_UNUSED,
+ jclass class_being_redefined ATTRIBUTE_UNUSED,
+ jobject loader ATTRIBUTE_UNUSED,
+ const char* name,
+ jobject protection_domain ATTRIBUTE_UNUSED,
+ jint class_data_len ATTRIBUTE_UNUSED,
+ const unsigned char* class_dat ATTRIBUTE_UNUSED,
+ jint* new_class_data_len,
+ unsigned char** new_class_data) {
+ std::string name_str(name);
+ if (gTransformations.find(name_str) != gTransformations.end() &&
+ gTransformations[name_str].size() > 0) {
+ CommonTransformationResult& res = gTransformations[name_str][0];
+ const std::vector<unsigned char>& desired_array = IsJVM() ? res.class_bytes : res.dex_bytes;
+ unsigned char* new_data;
+ CHECK_EQ(JVMTI_ERROR_NONE, jvmti_env->Allocate(desired_array.size(), &new_data));
+ memcpy(new_data, desired_array.data(), desired_array.size());
+ *new_class_data = new_data;
+ *new_class_data_len = desired_array.size();
+ gTransformations[name_str].pop_front();
+ }
+}
+
+extern "C" JNIEXPORT void Java_Main_enableCommonRetransformation(JNIEnv* env,
+ jclass,
+ jboolean enable) {
+ jvmtiError res = jvmti_env->SetEventNotificationMode(enable ? JVMTI_ENABLE : JVMTI_DISABLE,
+ JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
+ nullptr);
+ if (res != JVMTI_ERROR_NONE) {
+ JvmtiErrorToException(env, res);
+ }
+}
+
+static void throwRetransformationError(jvmtiEnv* jvmti,
+ JNIEnv* env,
+ jint num_targets,
+ jclass* targets,
+ jvmtiError res) {
+ return throwCommonRedefinitionError<false>(jvmti, env, num_targets, targets, res);
+}
+
+static void DoClassRetransformation(jvmtiEnv* jvmti_env, JNIEnv* env, jobjectArray targets) {
+ std::vector<jclass> classes;
+ jint len = env->GetArrayLength(targets);
+ for (jint i = 0; i < len; i++) {
+ classes.push_back(static_cast<jclass>(env->GetObjectArrayElement(targets, i)));
+ }
+ jvmtiError res = jvmti_env->RetransformClasses(len, classes.data());
+ if (res != JVMTI_ERROR_NONE) {
+ throwRetransformationError(jvmti_env, env, len, classes.data(), res);
+ }
+}
+
+// TODO Write something useful.
+extern "C" JNIEXPORT void JNICALL Java_Main_doCommonClassRetransformation(JNIEnv* env,
+ jclass,
+ jobjectArray targets) {
+ DoClassRetransformation(jvmti_env, env, targets);
+}
+
+// Get all capabilities except those related to retransformation.
jint OnLoad(JavaVM* vm,
char* options ATTRIBUTE_UNUSED,
void* reserved ATTRIBUTE_UNUSED) {
@@ -170,9 +317,135 @@
return 1;
}
SetAllCapabilities(jvmti_env);
+ jvmtiEventCallbacks cb;
+ memset(&cb, 0, sizeof(cb));
+ cb.ClassFileLoadHook = CommonClassFileLoadHookRetransformable;
+ if (jvmti_env->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
+ printf("Unable to set class file load hook cb!\n");
+ return 1;
+ }
return 0;
}
-} // namespace common_redefine
+} // namespace common_retransform
+
+static void BindMethod(jvmtiEnv* jenv,
+ JNIEnv* env,
+ jclass klass,
+ jmethodID method) {
+ char* name;
+ char* signature;
+ jvmtiError name_result = jenv->GetMethodName(method, &name, &signature, nullptr);
+ if (name_result != JVMTI_ERROR_NONE) {
+ LOG(FATAL) << "Could not get methods";
+ }
+
+ ArtMethod* m = jni::DecodeArtMethod(method);
+
+ std::string names[2];
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ names[0] = m->JniShortName();
+ names[1] = m->JniLongName();
+ }
+ for (const std::string& mangled_name : names) {
+ void* sym = dlsym(RTLD_DEFAULT, mangled_name.c_str());
+ if (sym == nullptr) {
+ continue;
+ }
+
+ JNINativeMethod native_method;
+ native_method.fnPtr = sym;
+ native_method.name = name;
+ native_method.signature = signature;
+
+ env->RegisterNatives(klass, &native_method, 1);
+
+ jenv->Deallocate(reinterpret_cast<unsigned char*>(name));
+ jenv->Deallocate(reinterpret_cast<unsigned char*>(signature));
+ return;
+ }
+
+ LOG(FATAL) << "Could not find " << names[0];
+}
+
+static jclass FindClassWithSystemClassLoader(JNIEnv* env, const char* class_name) {
+ // Find the system classloader.
+ ScopedLocalRef<jclass> cl_klass(env, env->FindClass("java/lang/ClassLoader"));
+ if (cl_klass.get() == nullptr) {
+ return nullptr;
+ }
+ jmethodID getsystemclassloader_method = env->GetStaticMethodID(cl_klass.get(),
+ "getSystemClassLoader",
+ "()Ljava/lang/ClassLoader;");
+ if (getsystemclassloader_method == nullptr) {
+ return nullptr;
+ }
+ ScopedLocalRef<jobject> cl(env, env->CallStaticObjectMethod(cl_klass.get(),
+ getsystemclassloader_method));
+ if (cl.get() == nullptr) {
+ return nullptr;
+ }
+
+ // Create a String of the name.
+ std::string descriptor = android::base::StringPrintf("L%s;", class_name);
+ std::string dot_name = DescriptorToDot(descriptor.c_str());
+ ScopedLocalRef<jstring> name_str(env, env->NewStringUTF(dot_name.c_str()));
+
+ // Call Class.forName with it.
+ ScopedLocalRef<jclass> c_klass(env, env->FindClass("java/lang/Class"));
+ if (c_klass.get() == nullptr) {
+ return nullptr;
+ }
+ jmethodID forname_method = env->GetStaticMethodID(
+ c_klass.get(),
+ "forName",
+ "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
+ if (forname_method == nullptr) {
+ return nullptr;
+ }
+
+ return reinterpret_cast<jclass>(env->CallStaticObjectMethod(c_klass.get(),
+ forname_method,
+ name_str.get(),
+ JNI_FALSE,
+ cl.get()));
+}
+
+void BindFunctions(jvmtiEnv* jenv, JNIEnv* env, const char* class_name) {
+ // Use JNI to load the class.
+ ScopedLocalRef<jclass> klass(env, env->FindClass(class_name));
+ if (klass.get() == nullptr) {
+ // We may be called with the wrong classloader. Try explicitly using the system classloader.
+ env->ExceptionClear();
+ klass.reset(FindClassWithSystemClassLoader(env, class_name));
+ if (klass.get() == nullptr) {
+ LOG(FATAL) << "Could not load " << class_name;
+ }
+ }
+
+ // Use JVMTI to get the methods.
+ jint method_count;
+ jmethodID* methods;
+ jvmtiError methods_result = jenv->GetClassMethods(klass.get(), &method_count, &methods);
+ if (methods_result != JVMTI_ERROR_NONE) {
+ LOG(FATAL) << "Could not get methods";
+ }
+
+ // Check each method.
+ for (jint i = 0; i < method_count; ++i) {
+ jint modifiers;
+ jvmtiError mod_result = jenv->GetMethodModifiers(methods[i], &modifiers);
+ if (mod_result != JVMTI_ERROR_NONE) {
+ LOG(FATAL) << "Could not get methods";
+ }
+ constexpr jint kNative = static_cast<jint>(kAccNative);
+ if ((modifiers & kNative) != 0) {
+ BindMethod(jenv, env, klass.get(), methods[i]);
+ }
+ }
+
+ jenv->Deallocate(reinterpret_cast<unsigned char*>(methods));
+}
} // namespace art
diff --git a/test/ti-agent/common_helper.h b/test/ti-agent/common_helper.h
index 642ca03..c60553d 100644
--- a/test/ti-agent/common_helper.h
+++ b/test/ti-agent/common_helper.h
@@ -27,6 +27,10 @@
jint OnLoad(JavaVM* vm, char* options, void* reserved);
} // namespace common_redefine
+namespace common_retransform {
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+} // namespace common_retransform
+
extern bool RuntimeIsJVM;
@@ -67,6 +71,12 @@
bool JvmtiErrorToException(JNIEnv* env, jvmtiError error);
+// Load the class through JNI. Inspect it, find all native methods. Construct the corresponding
+// mangled name, run dlsym and bind the method.
+//
+// This will abort on failure.
+void BindFunctions(jvmtiEnv* jvmti_env, JNIEnv* env, const char* class_name);
+
} // namespace art
#endif // ART_TEST_TI_AGENT_COMMON_HELPER_H_
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 521e672..8ed8e67 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "common_load.h"
+
#include <jni.h>
#include <stdio.h>
// TODO I don't know?
@@ -22,7 +24,6 @@
#include "art_method-inl.h"
#include "base/logging.h"
#include "base/macros.h"
-#include "common_load.h"
#include "common_helper.h"
#include "901-hello-ti-agent/basics.h"
@@ -32,6 +33,8 @@
jvmtiEnv* jvmti_env;
+namespace {
+
using OnLoad = jint (*)(JavaVM* vm, char* options, void* reserved);
using OnAttach = jint (*)(JavaVM* vm, char* options, void* reserved);
@@ -41,11 +44,50 @@
OnAttach attach;
};
+static void JNICALL VMInitCallback(jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread ATTRIBUTE_UNUSED) {
+ // Bind Main native methods.
+ BindFunctions(jvmti_env, jni_env, "Main");
+}
+
+// Install a phase callback that will bind JNI functions on VMInit.
+bool InstallBindCallback(JavaVM* vm) {
+ // Use a new jvmtiEnv. Otherwise we might collide with table changes.
+ jvmtiEnv* install_env;
+ if (vm->GetEnv(reinterpret_cast<void**>(&install_env), JVMTI_VERSION_1_0) != 0) {
+ return false;
+ }
+ SetAllCapabilities(install_env);
+
+ {
+ jvmtiEventCallbacks callbacks;
+ memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
+ callbacks.VMInit = VMInitCallback;
+
+ jvmtiError install_error = install_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
+ if (install_error != JVMTI_ERROR_NONE) {
+ return false;
+ }
+ }
+
+ {
+ jvmtiError enable_error = install_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_VM_INIT,
+ nullptr);
+ if (enable_error != JVMTI_ERROR_NONE) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
// A trivial OnLoad implementation that only initializes the global jvmti_env.
static jint MinimalOnLoad(JavaVM* vm,
char* options ATTRIBUTE_UNUSED,
void* reserved ATTRIBUTE_UNUSED) {
- if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+ if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0) != 0) {
printf("Unable to get jvmti env!\n");
return 1;
}
@@ -55,7 +97,7 @@
// A list of all non-standard the agents we have for testing. All other agents will use
// MinimalOnLoad.
-AgentLib agents[] = {
+static AgentLib agents[] = {
{ "901-hello-ti-agent", Test901HelloTi::OnLoad, nullptr },
{ "902-hello-transformation", common_redefine::OnLoad, nullptr },
{ "909-attach-agent", nullptr, Test909AttachAgent::OnAttach },
@@ -64,8 +106,10 @@
{ "916-obsolete-jit", common_redefine::OnLoad, nullptr },
{ "917-fields-transformation", common_redefine::OnLoad, nullptr },
{ "919-obsolete-fields", common_redefine::OnLoad, nullptr },
- { "921-hello-failure", common_redefine::OnLoad, nullptr },
+ { "921-hello-failure", common_retransform::OnLoad, nullptr },
{ "926-multi-obsolescence", common_redefine::OnLoad, nullptr },
+ { "930-hello-retransform", common_retransform::OnLoad, nullptr },
+ { "932-transform-saves", common_retransform::OnLoad, nullptr },
};
static AgentLib* FindAgent(char* name) {
@@ -99,6 +143,28 @@
RuntimeIsJVM = strncmp(options, "jvm", 3) == 0;
}
+static bool BindFunctionsAttached(JavaVM* vm, const char* class_name) {
+ // Get a JNIEnv. As the thread is attached, we must not destroy it.
+ JNIEnv* env;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != 0) {
+ printf("Unable to get JNI env!\n");
+ return false;
+ }
+
+ jvmtiEnv* jenv;
+ if (vm->GetEnv(reinterpret_cast<void**>(&jenv), JVMTI_VERSION_1_0) != 0) {
+ printf("Unable to get jvmti env!\n");
+ return false;
+ }
+ SetAllCapabilities(jenv);
+
+ BindFunctions(jenv, env, class_name);
+
+ return true;
+}
+
+} // namespace
+
extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
char* remaining_options = nullptr;
char* name_option = nullptr;
@@ -109,6 +175,10 @@
SetIsJVM(remaining_options);
+ if (!InstallBindCallback(vm)) {
+ return 1;
+ }
+
AgentLib* lib = FindAgent(name_option);
OnLoad fn = nullptr;
if (lib == nullptr) {
@@ -130,6 +200,9 @@
printf("Unable to find agent name in options: %s\n", options);
return -1;
}
+
+ BindFunctionsAttached(vm, "Main");
+
AgentLib* lib = FindAgent(name_option);
if (lib == nullptr) {
printf("Unable to find agent named: %s, add it to the list in test/ti-agent/common_load.cc\n",
diff --git a/test/ti-agent/common_load.h b/test/ti-agent/common_load.h
index fac94b4..d254421 100644
--- a/test/ti-agent/common_load.h
+++ b/test/ti-agent/common_load.h
@@ -17,6 +17,7 @@
#ifndef ART_TEST_TI_AGENT_COMMON_LOAD_H_
#define ART_TEST_TI_AGENT_COMMON_LOAD_H_
+#include "jni.h"
#include "openjdkjvmti/jvmti.h"
namespace art {
diff --git a/test/valgrind-suppressions.txt b/test/valgrind-suppressions.txt
index fd3c331..c775f98 100644
--- a/test/valgrind-suppressions.txt
+++ b/test/valgrind-suppressions.txt
@@ -22,3 +22,50 @@
...
fun:_ZN3art7Runtime17InitNativeMethodsEv
}
+
+# SigQuit runs libbacktrace
+{
+ BackTraceReading64
+ Memcheck:Addr8
+ fun:access_mem_unrestricted
+ fun:_Uelf64_memory_read
+ fun:_Uelf64_valid_object_memory
+ fun:map_create_list
+ fun:unw_map_local_create
+ fun:_ZN14UnwindMapLocal5BuildEv
+ fun:_ZN12BacktraceMap6CreateEib
+}
+{
+ BackTraceReading32
+ Memcheck:Addr4
+ fun:access_mem_unrestricted
+ fun:_Uelf32_memory_read
+ fun:_Uelf32_valid_object_memory
+ fun:map_create_list
+ fun:unw_map_local_create
+ fun:_ZN14UnwindMapLocal5BuildEv
+ fun:_ZN12BacktraceMap6CreateEib
+}
+{
+ BackTraceReading64
+ Memcheck:Addr8
+ fun:access_mem_unrestricted
+ fun:_Uelf64_memory_read
+ fun:_Uelf64_get_load_base
+ fun:map_create_list
+ fun:unw_map_local_create
+ fun:_ZN14UnwindMapLocal5BuildEv
+ fun:_ZN12BacktraceMap6CreateEib
+}
+{
+ BackTraceReading32
+ Memcheck:Addr4
+ fun:access_mem_unrestricted
+ fun:_Uelf32_memory_read
+ fun:_Uelf32_get_load_base
+ fun:map_create_list
+ fun:unw_map_local_create
+ fun:_ZN14UnwindMapLocal5BuildEv
+ fun:_ZN12BacktraceMap6CreateEib
+}
+
diff --git a/tools/dexfuzz/src/dexfuzz/Options.java b/tools/dexfuzz/src/dexfuzz/Options.java
index 99e03e8..af8a05c 100644
--- a/tools/dexfuzz/src/dexfuzz/Options.java
+++ b/tools/dexfuzz/src/dexfuzz/Options.java
@@ -50,6 +50,7 @@
public static String deviceName = "";
public static boolean usingSpecificDevice = false;
public static int repeat = 1;
+ public static int divergenceRetry = 10;
public static String executeDirectory = "/data/art-test";
public static String androidRoot = "";
public static String dumpMutationsFile = "mutations.dump";
@@ -118,6 +119,8 @@
Log.always(" --repeat=<n> : Fuzz N programs, executing each one.");
Log.always(" --short-timeouts : Shorten timeouts (faster; use if");
Log.always(" you want to focus on output divergences)");
+ Log.always(" --divergence-retry=<n> : Number of retries when checking if test is");
+ Log.always(" self-divergent. (Default: 10)");
Log.always(" --seed=<seed> : RNG seed to use");
Log.always(" --method-mutations=<n> : Maximum number of mutations to perform on each method.");
Log.always(" (Default: 3)");
@@ -239,6 +242,8 @@
maxMethods = Integer.parseInt(value);
} else if (key.equals("repeat")) {
repeat = Integer.parseInt(value);
+ } else if (key.equals("divergence-retry")) {
+ divergenceRetry = Integer.parseInt(value);
} else if (key.equals("log")) {
Log.setLoggingLevel(LogTag.valueOf(value.toUpperCase()));
} else if (key.equals("likelihoods")) {
@@ -360,6 +365,10 @@
Log.error("--repeat must be at least 1!");
return false;
}
+ if (divergenceRetry < 0) {
+ Log.error("--divergence-retry cannot be negative!");
+ return false;
+ }
if (usingProvidedSeed && repeat > 1) {
Log.error("Cannot use --repeat with --seed");
return false;
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java b/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java
index 1797d90..ccc426c 100644
--- a/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java
@@ -298,13 +298,13 @@
}
private boolean checkGoldenExecutorForSelfDivergence(String programName) {
- // Run golden executor 5 times, make sure it always produces
+ // Run golden executor multiple times, make sure it always produces
// the same output, otherwise report that it is self-divergent.
// TODO: Instead, produce a list of acceptable outputs, and see if the divergent
// outputs of the backends fall within this set of outputs.
String seenOutput = null;
- for (int i = 0; i < 5; i++) {
+ for (int i = 0; i < Options.divergenceRetry + 1; i++) {
goldenExecutor.reset();
goldenExecutor.execute(programName);
String output = goldenExecutor.getResult().getFlattenedOutput();
diff --git a/tools/stream-trace-converter.py b/tools/stream-trace-converter.py
index 951b05b..7e341f2 100755
--- a/tools/stream-trace-converter.py
+++ b/tools/stream-trace-converter.py
@@ -124,12 +124,20 @@
self._threads.append('%d\t%s\n' % (tid, str))
print 'New thread: %d/%s' % (tid, str)
+ def ProcessTraceSummary(self, input):
+ summaryLength = ReadIntLE(input)
+ str = input.read(summaryLength)
+ self._summary = str
+ print 'Summary: \"%s\"' % str
+
def ProcessSpecial(self, input):
code = ord(input.read(1))
if code == 1:
self.ProcessMethod(input)
elif code == 2:
self.ProcessThread(input)
+ elif code == 3:
+ self.ProcessTraceSummary(input)
else:
raise MyException("Unknown special!")
@@ -147,9 +155,24 @@
print 'Buffer underrun, file was probably truncated. Results should still be usable.'
def Finalize(self, header):
- header.write('*threads\n')
- for t in self._threads:
- header.write(t)
+ # If the summary is present in the input file, use it as the header except
+ # for the methods section which is emtpy in the input file. If not present,
+ # apppend header with the threads that are recorded in the input stream.
+ if (self._summary):
+ # Erase the contents that's already written earlier by PrintHeader.
+ header.seek(0)
+ header.truncate()
+ # Copy the lines from the input summary to the output header until
+ # the methods section is seen.
+ for line in self._summary.splitlines(True):
+ if line == "*methods\n":
+ break
+ else:
+ header.write(line)
+ else:
+ header.write('*threads\n')
+ for t in self._threads:
+ header.write(t)
header.write('*methods\n')
for m in self._methods:
header.write(m)
@@ -166,6 +189,7 @@
self._methods = []
self._threads = []
+ self._summary = None
self.Process(input, body)
self.Finalize(header)