summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk10
-rw-r--r--OWNERS3
-rw-r--r--build/Android.gtest.mk6
-rw-r--r--compiler/Android.bp10
-rw-r--r--compiler/driver/compiler_driver.cc38
-rw-r--r--compiler/optimizing/nodes_vector.h50
-rw-r--r--compiler/optimizing/nodes_vector_test.cc309
-rw-r--r--dexlayout/dexlayout.h3
-rw-r--r--runtime/class_linker.cc291
-rw-r--r--runtime/class_linker.h39
-rw-r--r--runtime/class_linker_test.cc106
-rw-r--r--runtime/common_runtime_test.cc59
-rw-r--r--runtime/common_runtime_test.h10
-rw-r--r--runtime/debugger.cc45
-rw-r--r--runtime/interpreter/interpreter_common.cc13
-rw-r--r--runtime/openjdkjvmti/OpenjdkJvmTi.cc25
-rw-r--r--runtime/openjdkjvmti/art_jvmti.h2
-rw-r--r--runtime/openjdkjvmti/jvmti_allocator.h10
-rw-r--r--runtime/openjdkjvmti/jvmti_weak_table.h7
-rw-r--r--runtime/openjdkjvmti/ti_allocator.cc48
-rw-r--r--runtime/openjdkjvmti/ti_allocator.h16
-rw-r--r--runtime/openjdkjvmti/ti_method.cc34
-rw-r--r--runtime/openjdkjvmti/ti_method.h5
-rw-r--r--runtime/well_known_classes.cc2
-rw-r--r--runtime/well_known_classes.h1
-rw-r--r--test/098-ddmc/src/Main.java60
-rw-r--r--test/141-class-unload/src/Main.java3
-rw-r--r--test/1900-track-alloc/alloc.cc159
-rw-r--r--test/1900-track-alloc/expected.txt0
-rw-r--r--test/1900-track-alloc/info.txt1
-rwxr-xr-xtest/1900-track-alloc/run17
-rw-r--r--test/1900-track-alloc/src/Main.java21
-rw-r--r--test/1900-track-alloc/src/art/Main.java32
-rw-r--r--test/1900-track-alloc/src/art/Test1900.java153
-rw-r--r--test/1901-get-bytecodes/bytecodes.cc63
-rw-r--r--test/1901-get-bytecodes/expected.txt0
-rw-r--r--test/1901-get-bytecodes/info.txt3
-rwxr-xr-xtest/1901-get-bytecodes/run17
-rw-r--r--test/1901-get-bytecodes/src/Main.java21
-rw-r--r--test/1901-get-bytecodes/src/art/Test1901.java147
-rw-r--r--test/596-app-images/src/Main.java88
-rw-r--r--test/988-method-trace/check22
-rw-r--r--test/988-method-trace/expected.txt236
-rw-r--r--test/988-method-trace/expected_jack.diff10
-rwxr-xr-xtest/988-method-trace/gen_srcs.py321
-rw-r--r--test/988-method-trace/src/art/Test988.java57
-rw-r--r--test/988-method-trace/src/art/Test988Intrinsics.java135
-rw-r--r--test/Android.bp3
-rw-r--r--test/Android.run-test-jvmti-java-library.mk171
-rw-r--r--test/ForClassLoaderA/Classes.java31
-rw-r--r--test/ForClassLoaderB/Classes.java30
-rw-r--r--test/ForClassLoaderC/Classes.java30
-rw-r--r--test/ForClassLoaderD/Classes.java27
-rw-r--r--test/knownfailures.json15
54 files changed, 2633 insertions, 382 deletions
diff --git a/Android.mk b/Android.mk
index 0402513cb5..7eb0bf926e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -81,7 +81,6 @@ include $(art_path)/tools/Android.mk
include $(art_path)/tools/ahat/Android.mk
include $(art_path)/tools/dexfuzz/Android.mk
include $(art_path)/libart_fake/Android.mk
-include $(art_path)/test/Android.run-test-jvmti-java-library.mk
ART_HOST_DEPENDENCIES := \
$(ART_HOST_EXECUTABLES) \
@@ -380,11 +379,12 @@ LOCAL_REQUIRED_MODULES += libart_fake
# * We will never add them if PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD = false.
# * We will always add them if PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD = true.
# * Otherwise, we will add them by default to userdebug and eng builds.
-ifneq (false,$(PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD))
-ifneq (,$(filter userdebug eng,$(PRODUCT_TARGET_BUILD_VARIANT)))
- PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD := true
+art_target_include_debug_build := $(PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD)
+ifneq (false,$(art_target_include_debug_build))
+ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+ art_target_include_debug_build := true
endif
-ifeq (true,$(PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD))
+ifeq (true,$(art_target_include_debug_build))
LOCAL_REQUIRED_MODULES += \
libartd \
libartd-compiler \
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000000..7297a14da2
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,3 @@
+ngeoffray@google.com
+sehr@google.com
+*
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c87abe5664..b6ffcc54f7 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -30,6 +30,10 @@ GTEST_DEX_DIRECTORIES := \
ErroneousA \
ErroneousB \
ErroneousInit \
+ ForClassLoaderA \
+ ForClassLoaderB \
+ ForClassLoaderC \
+ ForClassLoaderD \
ExceptionHandle \
GetMethodSignature \
ImageLayoutA \
@@ -99,7 +103,7 @@ $(ART_TEST_TARGET_GTEST_VerifierDepsMulti_DEX): $(ART_TEST_GTEST_VerifierDepsMul
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 := AllFields ErroneousA ErroneousB ErroneousInit Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
+ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB ErroneousInit ForClassLoaderA ForClassLoaderB ForClassLoaderC ForClassLoaderD 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
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 05c4553251..761f53fa5a 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -356,6 +356,7 @@ art_cc_test {
"optimizing/live_interval_test.cc",
"optimizing/loop_optimization_test.cc",
"optimizing/nodes_test.cc",
+ "optimizing/nodes_vector_test.cc",
"optimizing/parallel_move_test.cc",
"optimizing/pretty_printer_test.cc",
"optimizing/reference_type_propagation_test.cc",
@@ -429,13 +430,20 @@ art_cc_test {
shared_libs: [
"libartd-compiler",
- "libartd-simulator",
"libvixld-arm",
"libvixld-arm64",
"libbacktrace",
"libnativeloader",
],
+
+ target: {
+ host: {
+ shared_libs: [
+ "libartd-simulator",
+ ],
+ },
+ },
}
art_cc_test {
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index f834f30b84..bb64755c9e 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -87,6 +87,10 @@ static constexpr bool kTimeCompileMethod = !kIsDebugBuild;
// Print additional info during profile guided compilation.
static constexpr bool kDebugProfileGuidedCompilation = false;
+// Max encoded fields allowed for initializing app image. Hardcode the number for now
+// because 5000 should be large enough.
+static constexpr uint32_t kMaxEncodedFields = 5000;
+
static double Percentage(size_t x, size_t y) {
return 100.0 * (static_cast<double>(x)) / (static_cast<double>(x + y));
}
@@ -2273,11 +2277,17 @@ class InitializeClassVisitor : public CompilationVisitor {
}
// Otherwise it's in app image but superclasses can't be initialized, no need to proceed.
old_status = klass->GetStatus();
+
+ bool too_many_encoded_fields = false;
+ if (!is_boot_image && klass->NumStaticFields() > kMaxEncodedFields) {
+ too_many_encoded_fields = true;
+ }
// If the class was not initialized, we can proceed to see if we can initialize static
- // fields.
+ // fields. Limit the max number of encoded fields.
if (!klass->IsInitialized() &&
(is_app_image || is_boot_image) &&
is_superclass_initialized &&
+ !too_many_encoded_fields &&
manager_->GetCompiler()->IsImageClass(descriptor)) {
bool can_init_static_fields = false;
if (is_boot_image) {
@@ -2415,30 +2425,6 @@ class InitializeClassVisitor : public CompilationVisitor {
}
}
- bool NoPotentialInternStrings(Handle<mirror::Class> klass,
- Handle<mirror::ClassLoader>* class_loader)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::DexCache> h_dex_cache = hs.NewHandle(klass->GetDexCache());
- const DexFile* dex_file = h_dex_cache->GetDexFile();
- const DexFile::ClassDef* class_def = klass->GetClassDef();
- annotations::RuntimeEncodedStaticFieldValueIterator value_it(*dex_file,
- &h_dex_cache,
- class_loader,
- manager_->GetClassLinker(),
- *class_def);
-
- const auto jString = annotations::RuntimeEncodedStaticFieldValueIterator::kString;
- for ( ; value_it.HasNext(); value_it.Next()) {
- if (value_it.GetValueType() == jString) {
- // We don't want cache the static encoded strings which is a potential intern.
- return false;
- }
- }
-
- return true;
- }
-
bool ResolveTypesOfMethods(Thread* self, ArtMethod* m)
REQUIRES_SHARED(Locks::mutator_lock_) {
auto rtn_type = m->GetReturnType(true); // return value is discarded because resolve will be done internally.
@@ -2568,7 +2554,7 @@ class InitializeClassVisitor : public CompilationVisitor {
}
}
- return NoPotentialInternStrings(klass, class_loader);
+ return true;
}
const ParallelCompilationManager* const manager_;
diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h
index 5dbe29b4fa..dc522a463e 100644
--- a/compiler/optimizing/nodes_vector.h
+++ b/compiler/optimizing/nodes_vector.h
@@ -46,6 +46,10 @@ class Alignment {
return "ALIGN(" + std::to_string(base_) + "," + std::to_string(offset_) + ")";
}
+ bool operator==(const Alignment& other) const {
+ return base_ == other.base_ && offset_ == other.offset_;
+ }
+
private:
size_t base_;
size_t offset_;
@@ -96,6 +100,13 @@ class HVecOperation : public HVariableInputSizeInstruction {
return GetPackedField<TypeField>();
}
+ bool CanBeMoved() const OVERRIDE { return true; }
+
+ bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+ const HVecOperation* o = other->AsVecOperation();
+ return GetVectorLength() == o->GetVectorLength() && GetPackedType() == o->GetPackedType();
+ }
+
DECLARE_ABSTRACT_INSTRUCTION(VecOperation);
protected:
@@ -189,6 +200,11 @@ class HVecMemoryOperation : public HVecOperation {
HInstruction* GetArray() const { return InputAt(0); }
HInstruction* GetIndex() const { return InputAt(1); }
+ bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+ const HVecMemoryOperation* o = other->AsVecMemoryOperation();
+ return HVecOperation::InstructionDataEquals(o) && GetAlignment() == o->GetAlignment();
+ }
+
DECLARE_ABSTRACT_INSTRUCTION(VecMemoryOperation);
private:
@@ -378,6 +394,13 @@ class HVecHalvingAdd FINAL : public HVecBinaryOperation {
bool IsUnsigned() const { return GetPackedFlag<kFieldHAddIsUnsigned>(); }
bool IsRounded() const { return GetPackedFlag<kFieldHAddIsRounded>(); }
+ bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+ const HVecHalvingAdd* o = other->AsVecHalvingAdd();
+ return HVecOperation::InstructionDataEquals(o) &&
+ IsUnsigned() == o->IsUnsigned() &&
+ IsRounded() == o->IsRounded();
+ }
+
DECLARE_INSTRUCTION(VecHalvingAdd);
private:
@@ -466,6 +489,11 @@ class HVecMin FINAL : public HVecBinaryOperation {
bool IsUnsigned() const { return GetPackedFlag<kFieldMinOpIsUnsigned>(); }
+ bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+ const HVecMin* o = other->AsVecMin();
+ return HVecOperation::InstructionDataEquals(o) && IsUnsigned() == o->IsUnsigned();
+ }
+
DECLARE_INSTRUCTION(VecMin);
private:
@@ -496,6 +524,11 @@ class HVecMax FINAL : public HVecBinaryOperation {
bool IsUnsigned() const { return GetPackedFlag<kFieldMaxOpIsUnsigned>(); }
+ bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+ const HVecMax* o = other->AsVecMax();
+ return HVecOperation::InstructionDataEquals(o) && IsUnsigned() == o->IsUnsigned();
+ }
+
DECLARE_INSTRUCTION(VecMax);
private:
@@ -694,10 +727,9 @@ class HVecMultiplyAccumulate FINAL : public HVecOperation {
static constexpr int kInputMulLeftIndex = 1;
static constexpr int kInputMulRightIndex = 2;
- bool CanBeMoved() const OVERRIDE { return true; }
-
bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
- return op_kind_ == other->AsVecMultiplyAccumulate()->op_kind_;
+ const HVecMultiplyAccumulate* o = other->AsVecMultiplyAccumulate();
+ return HVecOperation::InstructionDataEquals(o) && GetOpKind() == o->GetOpKind();
}
InstructionKind GetOpKind() const { return op_kind_; }
@@ -732,10 +764,16 @@ class HVecLoad FINAL : public HVecMemoryOperation {
SetRawInputAt(1, index);
SetPackedFlag<kFieldIsStringCharAt>(is_string_char_at);
}
- DECLARE_INSTRUCTION(VecLoad);
bool IsStringCharAt() const { return GetPackedFlag<kFieldIsStringCharAt>(); }
+ bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+ const HVecLoad* o = other->AsVecLoad();
+ return HVecMemoryOperation::InstructionDataEquals(o) && IsStringCharAt() == o->IsStringCharAt();
+ }
+
+ DECLARE_INSTRUCTION(VecLoad);
+
private:
// Additional packed bits.
static constexpr size_t kFieldIsStringCharAt = HVecOperation::kNumberOfVectorOpPackedBits;
@@ -767,7 +805,11 @@ class HVecStore FINAL : public HVecMemoryOperation {
SetRawInputAt(1, index);
SetRawInputAt(2, value);
}
+
+ bool CanBeMoved() const OVERRIDE { return false; }
+
DECLARE_INSTRUCTION(VecStore);
+
private:
DISALLOW_COPY_AND_ASSIGN(HVecStore);
};
diff --git a/compiler/optimizing/nodes_vector_test.cc b/compiler/optimizing/nodes_vector_test.cc
new file mode 100644
index 0000000000..d33f8e5d2e
--- /dev/null
+++ b/compiler/optimizing/nodes_vector_test.cc
@@ -0,0 +1,309 @@
+/*
+ * 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 "base/arena_allocator.h"
+#include "nodes.h"
+#include "optimizing_unit_test.h"
+
+namespace art {
+
+/**
+ * Fixture class for testing vector nodes.
+ */
+class NodesVectorTest : public CommonCompilerTest {
+ public:
+ NodesVectorTest()
+ : pool_(),
+ allocator_(&pool_),
+ graph_(CreateGraph(&allocator_)) {
+ BuildGraph();
+ }
+
+ ~NodesVectorTest() { }
+
+ void BuildGraph() {
+ graph_->SetNumberOfVRegs(1);
+ entry_block_ = new (&allocator_) HBasicBlock(graph_);
+ exit_block_ = new (&allocator_) HBasicBlock(graph_);
+ graph_->AddBlock(entry_block_);
+ graph_->AddBlock(exit_block_);
+ graph_->SetEntryBlock(entry_block_);
+ graph_->SetExitBlock(exit_block_);
+ parameter_ = new (&allocator_) HParameterValue(graph_->GetDexFile(),
+ dex::TypeIndex(0),
+ 0,
+ Primitive::kPrimInt);
+ entry_block_->AddInstruction(parameter_);
+ }
+
+ // General building fields.
+ ArenaPool pool_;
+ ArenaAllocator allocator_;
+ HGraph* graph_;
+
+ HBasicBlock* entry_block_;
+ HBasicBlock* exit_block_;
+
+ HInstruction* parameter_;
+};
+
+//
+// The actual vector nodes tests.
+//
+
+TEST(NodesVector, Alignment) {
+ EXPECT_TRUE(Alignment(1, 0).IsAlignedAt(1));
+ EXPECT_FALSE(Alignment(1, 0).IsAlignedAt(2));
+
+ EXPECT_TRUE(Alignment(2, 0).IsAlignedAt(1));
+ EXPECT_TRUE(Alignment(2, 1).IsAlignedAt(1));
+ EXPECT_TRUE(Alignment(2, 0).IsAlignedAt(2));
+ EXPECT_FALSE(Alignment(2, 1).IsAlignedAt(2));
+ EXPECT_FALSE(Alignment(2, 0).IsAlignedAt(4));
+ EXPECT_FALSE(Alignment(2, 1).IsAlignedAt(4));
+
+ EXPECT_TRUE(Alignment(4, 0).IsAlignedAt(1));
+ EXPECT_TRUE(Alignment(4, 2).IsAlignedAt(1));
+ EXPECT_TRUE(Alignment(4, 0).IsAlignedAt(2));
+ EXPECT_TRUE(Alignment(4, 2).IsAlignedAt(2));
+ EXPECT_TRUE(Alignment(4, 0).IsAlignedAt(4));
+ EXPECT_FALSE(Alignment(4, 2).IsAlignedAt(4));
+ EXPECT_FALSE(Alignment(4, 0).IsAlignedAt(8));
+ EXPECT_FALSE(Alignment(4, 2).IsAlignedAt(8));
+
+ EXPECT_TRUE(Alignment(16, 0).IsAlignedAt(1));
+ EXPECT_TRUE(Alignment(16, 0).IsAlignedAt(2));
+ EXPECT_TRUE(Alignment(16, 0).IsAlignedAt(4));
+ EXPECT_TRUE(Alignment(16, 8).IsAlignedAt(8));
+ EXPECT_TRUE(Alignment(16, 0).IsAlignedAt(16));
+ EXPECT_FALSE(Alignment(16, 1).IsAlignedAt(16));
+ EXPECT_FALSE(Alignment(16, 7).IsAlignedAt(16));
+ EXPECT_FALSE(Alignment(16, 0).IsAlignedAt(32));
+}
+
+TEST(NodesVector, AlignmentEQ) {
+ EXPECT_TRUE(Alignment(2, 0) == Alignment(2, 0));
+ EXPECT_TRUE(Alignment(2, 1) == Alignment(2, 1));
+ EXPECT_TRUE(Alignment(4, 0) == Alignment(4, 0));
+ EXPECT_TRUE(Alignment(4, 2) == Alignment(4, 2));
+
+ EXPECT_FALSE(Alignment(4, 0) == Alignment(2, 0));
+ EXPECT_FALSE(Alignment(4, 0) == Alignment(4, 1));
+ EXPECT_FALSE(Alignment(4, 0) == Alignment(8, 0));
+}
+
+TEST(NodesVector, AlignmentString) {
+ EXPECT_STREQ("ALIGN(1,0)", Alignment(1, 0).ToString().c_str());
+
+ EXPECT_STREQ("ALIGN(2,0)", Alignment(2, 0).ToString().c_str());
+ EXPECT_STREQ("ALIGN(2,1)", Alignment(2, 1).ToString().c_str());
+
+ EXPECT_STREQ("ALIGN(16,0)", Alignment(16, 0).ToString().c_str());
+ EXPECT_STREQ("ALIGN(16,1)", Alignment(16, 1).ToString().c_str());
+ EXPECT_STREQ("ALIGN(16,8)", Alignment(16, 8).ToString().c_str());
+ EXPECT_STREQ("ALIGN(16,9)", Alignment(16, 9).ToString().c_str());
+}
+
+TEST_F(NodesVectorTest, VectorOperationProperties) {
+ HVecOperation* v0 = new (&allocator_)
+ HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 4);
+ HVecOperation* v1 = new (&allocator_)
+ HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 4);
+ HVecOperation* v2 = new (&allocator_)
+ HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 2);
+ HVecOperation* v3 = new (&allocator_)
+ HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimShort, 4);
+ HVecOperation* v4 = new (&allocator_)
+ HVecStore(&allocator_, parameter_, parameter_, v0, Primitive::kPrimInt, 4);
+
+ EXPECT_TRUE(v0->Equals(v0));
+ EXPECT_TRUE(v1->Equals(v1));
+ EXPECT_TRUE(v2->Equals(v2));
+ EXPECT_TRUE(v3->Equals(v3));
+ EXPECT_TRUE(v4->Equals(v4));
+
+ EXPECT_TRUE(v0->Equals(v1));
+ EXPECT_FALSE(v0->Equals(v2)); // different vector lengths
+ EXPECT_FALSE(v0->Equals(v3)); // different packed types
+ EXPECT_FALSE(v0->Equals(v4)); // different kinds
+
+ EXPECT_TRUE(v1->Equals(v0)); // switch operands
+ EXPECT_FALSE(v4->Equals(v0));
+
+ EXPECT_EQ(4u, v0->GetVectorLength());
+ EXPECT_EQ(4u, v1->GetVectorLength());
+ EXPECT_EQ(2u, v2->GetVectorLength());
+ EXPECT_EQ(4u, v3->GetVectorLength());
+ EXPECT_EQ(4u, v4->GetVectorLength());
+
+ EXPECT_EQ(Primitive::kPrimDouble, v0->GetType());
+ EXPECT_EQ(Primitive::kPrimDouble, v1->GetType());
+ EXPECT_EQ(Primitive::kPrimDouble, v2->GetType());
+ EXPECT_EQ(Primitive::kPrimDouble, v3->GetType());
+ EXPECT_EQ(Primitive::kPrimDouble, v4->GetType());
+
+ EXPECT_EQ(Primitive::kPrimInt, v0->GetPackedType());
+ EXPECT_EQ(Primitive::kPrimInt, v1->GetPackedType());
+ EXPECT_EQ(Primitive::kPrimInt, v2->GetPackedType());
+ EXPECT_EQ(Primitive::kPrimShort, v3->GetPackedType());
+ EXPECT_EQ(Primitive::kPrimInt, v4->GetPackedType());
+
+ EXPECT_EQ(16u, v0->GetVectorNumberOfBytes());
+ EXPECT_EQ(16u, v1->GetVectorNumberOfBytes());
+ EXPECT_EQ(8u, v2->GetVectorNumberOfBytes());
+ EXPECT_EQ(8u, v3->GetVectorNumberOfBytes());
+ EXPECT_EQ(16u, v4->GetVectorNumberOfBytes());
+
+ EXPECT_TRUE(v0->CanBeMoved());
+ EXPECT_TRUE(v1->CanBeMoved());
+ EXPECT_TRUE(v2->CanBeMoved());
+ EXPECT_TRUE(v3->CanBeMoved());
+ EXPECT_FALSE(v4->CanBeMoved());
+}
+
+TEST_F(NodesVectorTest, VectorAlignmentAndStringCharAtMatterOnLoad) {
+ HVecLoad* v0 = new (&allocator_)
+ HVecLoad(&allocator_, parameter_, parameter_, Primitive::kPrimInt, 4, /*is_string_char_at*/ false);
+ HVecLoad* v1 = new (&allocator_)
+ HVecLoad(&allocator_, parameter_, parameter_, Primitive::kPrimInt, 4, /*is_string_char_at*/ false);
+ HVecLoad* v2 = new (&allocator_)
+ HVecLoad(&allocator_, parameter_, parameter_, Primitive::kPrimInt, 4, /*is_string_char_at*/ true);
+
+ EXPECT_FALSE(v0->IsStringCharAt());
+ EXPECT_FALSE(v1->IsStringCharAt());
+ EXPECT_TRUE(v2->IsStringCharAt());
+
+ EXPECT_TRUE(v0->Equals(v0));
+ EXPECT_TRUE(v1->Equals(v1));
+ EXPECT_TRUE(v2->Equals(v2));
+
+ EXPECT_TRUE(v0->Equals(v1));
+ EXPECT_FALSE(v0->Equals(v2));
+
+ EXPECT_TRUE(v0->GetAlignment() == Alignment(4, 0));
+ EXPECT_TRUE(v1->GetAlignment() == Alignment(4, 0));
+ EXPECT_TRUE(v2->GetAlignment() == Alignment(4, 0));
+
+ v1->SetAlignment(Alignment(8, 0));
+
+ EXPECT_TRUE(v1->GetAlignment() == Alignment(8, 0));
+
+ EXPECT_FALSE(v0->Equals(v1)); // no longer equal
+}
+
+TEST_F(NodesVectorTest, VectorSignMattersOnMin) {
+ HVecOperation* v0 = new (&allocator_)
+ HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 4);
+
+ HVecMin* v1 = new (&allocator_)
+ HVecMin(&allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ true);
+ HVecMin* v2 = new (&allocator_)
+ HVecMin(&allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ false);
+ HVecMin* v3 = new (&allocator_)
+ HVecMin(&allocator_, v0, v0, Primitive::kPrimInt, 2, /*is_unsigned*/ true);
+
+ EXPECT_TRUE(v1->IsUnsigned());
+ EXPECT_FALSE(v2->IsUnsigned());
+ EXPECT_TRUE(v3->IsUnsigned());
+
+ EXPECT_TRUE(v1->Equals(v1));
+ EXPECT_TRUE(v2->Equals(v2));
+ EXPECT_TRUE(v3->Equals(v3));
+
+ EXPECT_FALSE(v1->Equals(v2)); // different signs
+ EXPECT_FALSE(v1->Equals(v3)); // different vector lengths
+}
+
+TEST_F(NodesVectorTest, VectorSignMattersOnMax) {
+ HVecOperation* v0 = new (&allocator_)
+ HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 4);
+
+ HVecMax* v1 = new (&allocator_)
+ HVecMax(&allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ true);
+ HVecMax* v2 = new (&allocator_)
+ HVecMax(&allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ false);
+ HVecMax* v3 = new (&allocator_)
+ HVecMax(&allocator_, v0, v0, Primitive::kPrimInt, 2, /*is_unsigned*/ true);
+
+ EXPECT_TRUE(v1->IsUnsigned());
+ EXPECT_FALSE(v2->IsUnsigned());
+ EXPECT_TRUE(v3->IsUnsigned());
+
+ EXPECT_TRUE(v1->Equals(v1));
+ EXPECT_TRUE(v2->Equals(v2));
+ EXPECT_TRUE(v3->Equals(v3));
+
+ EXPECT_FALSE(v1->Equals(v2)); // different signs
+ EXPECT_FALSE(v1->Equals(v3)); // different vector lengths
+}
+
+TEST_F(NodesVectorTest, VectorAttributesMatterOnHalvingAdd) {
+ HVecOperation* v0 = new (&allocator_)
+ HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 4);
+
+ HVecHalvingAdd* v1 = new (&allocator_) HVecHalvingAdd(
+ &allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ true, /*is_rounded*/ true);
+ HVecHalvingAdd* v2 = new (&allocator_) HVecHalvingAdd(
+ &allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ true, /*is_rounded*/ false);
+ HVecHalvingAdd* v3 = new (&allocator_) HVecHalvingAdd(
+ &allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ false, /*is_rounded*/ true);
+ HVecHalvingAdd* v4 = new (&allocator_) HVecHalvingAdd(
+ &allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ false, /*is_rounded*/ false);
+ HVecHalvingAdd* v5 = new (&allocator_) HVecHalvingAdd(
+ &allocator_, v0, v0, Primitive::kPrimInt, 2, /*is_unsigned*/ true, /*is_rounded*/ true);
+
+ EXPECT_TRUE(v1->Equals(v1));
+ EXPECT_TRUE(v2->Equals(v2));
+ EXPECT_TRUE(v3->Equals(v3));
+ EXPECT_TRUE(v4->Equals(v4));
+ EXPECT_TRUE(v5->Equals(v5));
+
+ EXPECT_TRUE(v1->IsUnsigned() && v1->IsRounded());
+ EXPECT_TRUE(v2->IsUnsigned() && !v2->IsRounded());
+ EXPECT_TRUE(!v3->IsUnsigned() && v3->IsRounded());
+ EXPECT_TRUE(!v4->IsUnsigned() && !v4->IsRounded());
+ EXPECT_TRUE(v5->IsUnsigned() && v5->IsRounded());
+
+ EXPECT_FALSE(v1->Equals(v2)); // different attributes
+ EXPECT_FALSE(v1->Equals(v3)); // different attributes
+ EXPECT_FALSE(v1->Equals(v4)); // different attributes
+ EXPECT_FALSE(v1->Equals(v5)); // different vector lengths
+}
+
+TEST_F(NodesVectorTest, VectorOperationMattersOnMultiplyAccumulate) {
+ HVecOperation* v0 = new (&allocator_)
+ HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 4);
+
+ HVecMultiplyAccumulate* v1 = new (&allocator_)
+ HVecMultiplyAccumulate(&allocator_, HInstruction::kAdd, v0, v0, v0, Primitive::kPrimInt, 4);
+ HVecMultiplyAccumulate* v2 = new (&allocator_)
+ HVecMultiplyAccumulate(&allocator_, HInstruction::kSub, v0, v0, v0, Primitive::kPrimInt, 4);
+ HVecMultiplyAccumulate* v3 = new (&allocator_)
+ HVecMultiplyAccumulate(&allocator_, HInstruction::kAdd, v0, v0, v0, Primitive::kPrimInt, 2);
+
+ EXPECT_EQ(HInstruction::kAdd, v1->GetOpKind());
+ EXPECT_EQ(HInstruction::kSub, v2->GetOpKind());
+ EXPECT_EQ(HInstruction::kAdd, v3->GetOpKind());
+
+ EXPECT_TRUE(v1->Equals(v1));
+ EXPECT_TRUE(v2->Equals(v2));
+ EXPECT_TRUE(v3->Equals(v3));
+
+ EXPECT_FALSE(v1->Equals(v2)); // different operators
+ EXPECT_FALSE(v1->Equals(v3)); // different vector lengths
+}
+
+} // namespace art
diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h
index 531bc98a0c..ed011d6771 100644
--- a/dexlayout/dexlayout.h
+++ b/dexlayout/dexlayout.h
@@ -58,7 +58,8 @@ class Options {
bool show_section_headers_ = false;
bool show_section_statistics_ = false;
bool verbose_ = false;
- bool verify_output_ = false;
+ // TODO: Set verify_output_ back to false by default. Was set to true for debugging b/62840842.
+ bool verify_output_ = true;
bool visualize_pattern_ = false;
OutputFormat output_format_ = kOutputPlain;
const char* output_dex_directory_ = nullptr;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 7aab9de1ed..a19085f5b5 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -55,6 +55,7 @@
#include "gc_root-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/heap_bitmap-inl.h"
+#include "gc/accounting/space_bitmap-inl.h"
#include "gc/heap.h"
#include "gc/scoped_gc_critical_section.h"
#include "gc/space/image_space.h"
@@ -88,6 +89,7 @@
#include "mirror/method_handles_lookup.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
+#include "mirror/object-refvisitor-inl.h"
#include "mirror/proxy.h"
#include "mirror/reference-inl.h"
#include "mirror/stack_trace_element.h"
@@ -1193,6 +1195,63 @@ class VerifyDeclaringClassVisitor : public ArtMethodVisitor {
gc::accounting::HeapBitmap* const live_bitmap_;
};
+class FixupInternVisitor {
+ public:
+ ALWAYS_INLINE ObjPtr<mirror::Object> TryInsertIntern(mirror::Object* obj) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (obj != nullptr && obj->IsString()) {
+ const auto intern = Runtime::Current()->GetInternTable()->InternStrong(obj->AsString());
+ return intern;
+ }
+ return obj;
+ }
+
+ ALWAYS_INLINE void VisitRootIfNonNull(
+ mirror::CompressedReference<mirror::Object>* root) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!root->IsNull()) {
+ VisitRoot(root);
+ }
+ }
+
+ ALWAYS_INLINE void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ root->Assign(TryInsertIntern(root->AsMirrorPtr()));
+ }
+
+ // Visit Class Fields
+ ALWAYS_INLINE void operator()(ObjPtr<mirror::Object> obj,
+ MemberOffset offset,
+ bool is_static ATTRIBUTE_UNUSED) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // There could be overlap between ranges, we must avoid visiting the same reference twice.
+ // Avoid the class field since we already fixed it up in FixupClassVisitor.
+ if (offset.Uint32Value() != mirror::Object::ClassOffset().Uint32Value()) {
+ // Updating images, don't do a read barrier.
+ // Only string fields are fixed, don't do a verify.
+ mirror::Object* ref = obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(
+ offset);
+ obj->SetFieldObject<false, false>(offset, TryInsertIntern(ref));
+ }
+ }
+
+ void operator()(ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED,
+ ObjPtr<mirror::Reference> ref) const
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
+ this->operator()(ref, mirror::Reference::ReferentOffset(), false);
+ }
+
+ void operator()(mirror::Object* obj) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (obj->IsDexCache()) {
+ obj->VisitReferences<true, kVerifyNone, kWithoutReadBarrier>(*this, *this);
+ } else {
+ // Don't visit native roots for non-dex-cache
+ obj->VisitReferences<false, kVerifyNone, kWithoutReadBarrier>(*this, *this);
+ }
+ }
+};
+
// Copies data from one array to another array at the same position
// if pred returns false. If there is a page of continuous data in
// the src array for which pred consistently returns true then
@@ -1285,6 +1344,7 @@ bool AppImageClassLoadersAndDexCachesHelper::Update(
return false;
}
}
+
// Only add the classes to the class loader after the points where we can return false.
for (size_t i = 0; i < num_dex_caches; i++) {
ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i);
@@ -1448,6 +1508,21 @@ bool AppImageClassLoadersAndDexCachesHelper::Update(
}
}
}
+ {
+ // Fixup all the literal strings happens at app images which are supposed to be interned.
+ ScopedTrace timing("Fixup String Intern in image and dex_cache");
+ const auto& image_header = space->GetImageHeader();
+ const auto bitmap = space->GetMarkBitmap(); // bitmap of objects
+ const uint8_t* target_base = space->GetMemMap()->Begin();
+ const ImageSection& objects_section =
+ image_header.GetImageSection(ImageHeader::kSectionObjects);
+
+ uintptr_t objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset());
+ uintptr_t objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End());
+
+ FixupInternVisitor fixup_intern_visitor;
+ bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_intern_visitor);
+ }
if (*out_forward_dex_cache_array) {
ScopedTrace timing("Fixup ArtMethod dex cache arrays");
FixupArtMethodArrayVisitor visitor(header);
@@ -2410,74 +2485,121 @@ ClassPathEntry FindInClassPath(const char* descriptor,
return ClassPathEntry(nullptr, nullptr);
}
+// Returns true if the given class loader is either a PathClassLoader or a DexClassLoader.
+// (they both have the same behaviour with respect to class lockup order)
+static bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+ Handle<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ mirror::Class* class_loader_class = class_loader->GetClass();
+ return
+ (class_loader_class ==
+ soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)) ||
+ (class_loader_class ==
+ soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader));
+}
+
+static bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+ Handle<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ mirror::Class* class_loader_class = class_loader->GetClass();
+ return class_loader_class ==
+ soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader);
+}
+
bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
ObjPtr<mirror::Class>* result) {
- // Termination case: boot class-loader.
+ // Termination case: boot class loader.
if (IsBootClassLoader(soa, class_loader.Get())) {
- // The boot class loader, search the boot class path.
- ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
- if (pair.second != nullptr) {
- ObjPtr<mirror::Class> klass = LookupClass(self, descriptor, hash, nullptr);
- if (klass != nullptr) {
- *result = EnsureResolved(self, descriptor, klass);
- } else {
- *result = DefineClass(self,
- descriptor,
- hash,
- ScopedNullHandle<mirror::ClassLoader>(),
- *pair.first,
- *pair.second);
- }
- if (*result == nullptr) {
- CHECK(self->IsExceptionPending()) << descriptor;
- self->ClearException();
- }
- } else {
- *result = nullptr;
- }
+ *result = FindClassInBootClassLoaderClassPath(self, descriptor, hash);
return true;
}
- // Unsupported class-loader?
- if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader) !=
- class_loader->GetClass()) {
- // PathClassLoader is the most common case, so it's the one we check first. For secondary dex
- // files, we also check DexClassLoader here.
- if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader) !=
- class_loader->GetClass()) {
- *result = nullptr;
- return false;
+ if (IsPathOrDexClassLoader(soa, class_loader)) {
+ // For regular path or dex class loader the search order is:
+ // - parent
+ // - class loader dex files
+
+ // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
+ if (!FindClassInBaseDexClassLoader(soa, self, descriptor, hash, h_parent, result)) {
+ return false; // One of the parents is not supported.
+ }
+ if (*result != nullptr) {
+ return true; // Found the class up the chain.
}
+
+ // Search the current class loader classpath.
+ *result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader);
+ return true;
}
- // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
- StackHandleScope<4> hs(self);
- Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
- bool recursive_result = FindClassInBaseDexClassLoader(soa,
- self,
- descriptor,
- hash,
- h_parent,
- result);
+ if (IsDelegateLastClassLoader(soa, class_loader)) {
+ // For delegate last, the search order is:
+ // - boot class path
+ // - class loader dex files
+ // - parent
+ *result = FindClassInBootClassLoaderClassPath(self, descriptor, hash);
+ if (*result != nullptr) {
+ return true; // The class is part of the boot class path.
+ }
- if (!recursive_result) {
- // Something wrong up the chain.
- return false;
+ *result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader);
+ if (*result != nullptr) {
+ return true; // Found the class in the current class loader
+ }
+
+ // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
+ return FindClassInBaseDexClassLoader(soa, self, descriptor, hash, h_parent, result);
}
- if (*result != nullptr) {
- // Found the class up the chain.
- return true;
+ // Unsupported class loader.
+ *result = nullptr;
+ return false;
+}
+
+// Finds the class in the boot class loader.
+// If the class is found the method returns the resolved class. Otherwise it returns null.
+ObjPtr<mirror::Class> ClassLinker::FindClassInBootClassLoaderClassPath(Thread* self,
+ const char* descriptor,
+ size_t hash) {
+ ObjPtr<mirror::Class> result = nullptr;
+ ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
+ if (pair.second != nullptr) {
+ ObjPtr<mirror::Class> klass = LookupClass(self, descriptor, hash, nullptr);
+ if (klass != nullptr) {
+ result = EnsureResolved(self, descriptor, klass);
+ } else {
+ result = DefineClass(self,
+ descriptor,
+ hash,
+ ScopedNullHandle<mirror::ClassLoader>(),
+ *pair.first,
+ *pair.second);
+ }
+ if (result == nullptr) {
+ CHECK(self->IsExceptionPending()) << descriptor;
+ self->ClearException();
+ }
}
+ return result;
+}
+
+ObjPtr<mirror::Class> ClassLinker::FindClassInBaseDexClassLoaderClassPath(
+ ScopedObjectAccessAlreadyRunnable& soa,
+ const char* descriptor,
+ size_t hash,
+ Handle<mirror::ClassLoader> class_loader) {
+ CHECK(IsPathOrDexClassLoader(soa, class_loader) || IsDelegateLastClassLoader(soa, class_loader))
+ << "Unexpected class loader for descriptor " << descriptor;
- // Handle this step.
- // Handle as if this is the child PathClassLoader.
- // The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
- // We need to get the DexPathList and loop through it.
+ Thread* self = soa.Self();
ArtField* const cookie_field =
jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
ArtField* const dex_file_field =
@@ -2489,10 +2611,11 @@ bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnabl
// DexPathList has an array dexElements of Elements[] which each contain a dex file.
ObjPtr<mirror::Object> dex_elements_obj =
jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
- GetObject(dex_path_list);
+ GetObject(dex_path_list);
// Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
// at the mCookie which is a DexFile vector.
if (dex_elements_obj != nullptr) {
+ StackHandleScope<1> hs(self);
Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
@@ -2518,19 +2641,18 @@ bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnabl
OatDexFile::FindClassDef(*cp_dex_file, descriptor, hash);
if (dex_class_def != nullptr) {
ObjPtr<mirror::Class> klass = DefineClass(self,
- descriptor,
- hash,
- class_loader,
- *cp_dex_file,
- *dex_class_def);
+ descriptor,
+ hash,
+ class_loader,
+ *cp_dex_file,
+ *dex_class_def);
if (klass == nullptr) {
CHECK(self->IsExceptionPending()) << descriptor;
self->ClearException();
// TODO: Is it really right to break here, and not check the other dex files?
- return true;
+ return nullptr;
}
- *result = klass;
- return true;
+ return klass;
}
}
}
@@ -2538,9 +2660,7 @@ bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnabl
}
self->AssertNoPendingException();
}
-
- // Result is still null from the parent call, no need to set it again...
- return true;
+ return nullptr;
}
mirror::Class* ClassLinker::FindClass(Thread* self,
@@ -8640,8 +8760,15 @@ const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) {
return descriptor;
}
-jobject ClassLinker::CreatePathClassLoader(Thread* self,
- const std::vector<const DexFile*>& dex_files) {
+jobject ClassLinker::CreateWellKnownClassLoader(Thread* self,
+ const std::vector<const DexFile*>& dex_files,
+ jclass loader_class,
+ jobject parent_loader) {
+ CHECK(self->GetJniEnv()->IsSameObject(loader_class,
+ WellKnownClasses::dalvik_system_PathClassLoader) ||
+ self->GetJniEnv()->IsSameObject(loader_class,
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader));
+
// SOAAlreadyRunnable is protected, and we need something to add a global reference.
// We could move the jobject to the callers, but all call-sites do this...
ScopedObjectAccessUnchecked soa(self);
@@ -8677,8 +8804,8 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self,
for (const DexFile* dex_file : dex_files) {
StackHandleScope<4> hs2(self);
- // CreatePathClassLoader is only used by gtests. Index 0 of h_long_array is supposed to be the
- // oat file but we can leave it null.
+ // CreateWellKnownClassLoader is only used by gtests and compiler.
+ // Index 0 of h_long_array is supposed to be the oat file but we can leave it null.
Handle<mirror::LongArray> h_long_array = hs2.NewHandle(mirror::LongArray::Alloc(
self,
kDexFileIndexStart + 1));
@@ -8713,36 +8840,44 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self,
// Set elements.
dex_elements_field->SetObject<false>(h_dex_path_list.Get(), h_dex_elements.Get());
- // Create PathClassLoader.
- Handle<mirror::Class> h_path_class_class = hs.NewHandle(
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader));
- Handle<mirror::Object> h_path_class_loader = hs.NewHandle(
- h_path_class_class->AllocObject(self));
- DCHECK(h_path_class_loader != nullptr);
+ // Create the class loader..
+ Handle<mirror::Class> h_loader_class = hs.NewHandle(soa.Decode<mirror::Class>(loader_class));
+ Handle<mirror::Object> h_class_loader = hs.NewHandle(h_loader_class->AllocObject(self));
+ DCHECK(h_class_loader != nullptr);
// Set DexPathList.
ArtField* path_list_field =
jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList);
DCHECK(path_list_field != nullptr);
- path_list_field->SetObject<false>(h_path_class_loader.Get(), h_dex_path_list.Get());
+ path_list_field->SetObject<false>(h_class_loader.Get(), h_dex_path_list.Get());
// Make a pretend boot-classpath.
// TODO: Should we scan the image?
ArtField* const parent_field =
mirror::Class::FindField(self,
- h_path_class_loader->GetClass(),
+ h_class_loader->GetClass(),
"parent",
"Ljava/lang/ClassLoader;");
DCHECK(parent_field != nullptr);
- ObjPtr<mirror::Object> boot_cl =
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self);
- parent_field->SetObject<false>(h_path_class_loader.Get(), boot_cl);
+
+ ObjPtr<mirror::Object> parent = (parent_loader != nullptr)
+ ? soa.Decode<mirror::ClassLoader>(parent_loader)
+ : soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self);
+ parent_field->SetObject<false>(h_class_loader.Get(), parent);
// Make it a global ref and return.
ScopedLocalRef<jobject> local_ref(
- soa.Env(), soa.Env()->AddLocalReference<jobject>(h_path_class_loader.Get()));
+ soa.Env(), soa.Env()->AddLocalReference<jobject>(h_class_loader.Get()));
return soa.Env()->NewGlobalRef(local_ref.get());
}
+jobject ClassLinker::CreatePathClassLoader(Thread* self,
+ const std::vector<const DexFile*>& dex_files) {
+ return CreateWellKnownClassLoader(self,
+ dex_files,
+ WellKnownClasses::dalvik_system_PathClassLoader,
+ nullptr);
+}
+
void ClassLinker::DropFindArrayClassCache() {
std::fill_n(find_array_class_cache_, kFindArrayCacheSize, GcRoot<mirror::Class>(nullptr));
find_array_class_cache_next_victim_ = 0;
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 1e8125eb05..de1fefd20e 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -553,8 +553,24 @@ class ClassLinker {
REQUIRES(!Locks::classlinker_classes_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Creates a GlobalRef PathClassLoader that can be used to load classes from the given dex files.
+ // Creates a GlobalRef PathClassLoader or DelegateLastClassLoader (specified by loader_class)
+ // that can be used to load classes from the given dex files. The parent of the class loader
+ // will be set to `parent_loader`. If `parent_loader` is null the parent will be
+ // the boot class loader.
+ // If class_loader points to a different class than PathClassLoader or DelegateLastClassLoader
+ // this method will abort.
// Note: the objects are not completely set up. Do not use this outside of tests and the compiler.
+ jobject CreateWellKnownClassLoader(Thread* self,
+ const std::vector<const DexFile*>& dex_files,
+ jclass loader_class,
+ jobject parent_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_);
+
+ // Calls CreateWellKnownClassLoader(self,
+ // dex_files,
+ // WellKnownClasses::dalvik_system_PathClassLoader,
+ // nullptr)
jobject CreatePathClassLoader(Thread* self, const std::vector<const DexFile*>& dex_files)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
@@ -819,6 +835,27 @@ class ClassLinker {
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
+ // Finds the class in the classpath of the given class loader. It only searches the class loader
+ // dex files and does not recurse into its parent.
+ // The method checks that the provided class loader is either a PathClassLoader or a
+ // DexClassLoader.
+ // If the class is found the method returns the resolved class. Otherwise it returns null.
+ ObjPtr<mirror::Class> FindClassInBaseDexClassLoaderClassPath(
+ ScopedObjectAccessAlreadyRunnable& soa,
+ const char* descriptor,
+ size_t hash,
+ Handle<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_);
+
+ // Finds the class in the boot class loader.
+ // If the class is found the method returns the resolved class. Otherwise it returns null.
+ ObjPtr<mirror::Class> FindClassInBootClassLoaderClassPath(Thread* self,
+ const char* descriptor,
+ size_t hash)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_);
+
// Finds a class by its descriptor, returning NULL if it isn't wasn't loaded
// by the given 'class_loader'. Uses the provided hash for the descriptor.
mirror::Class* LookupClass(Thread* self,
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 684a261cca..03cc6c59c4 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -1533,4 +1533,110 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) {
ASSERT_TRUE(method1_type.Get() != method2_type.Get());
}
+// Verify that ClassLinker's CreateWellknownClassLoader works as expected
+// by creating a chain of class loaders with various dex files.
+TEST_F(ClassLinkerTest, CreateWellKnownClassLoader) {
+ // LoadDexIn*ClassLoader methods already assert that the parent loader is the expected one.
+ // No need to check again.
+ jobject class_loader_a = LoadDexInPathClassLoader("MyClass", nullptr);
+ jobject class_loader_b = LoadDexInDelegateLastClassLoader("Nested", class_loader_a);
+ jobject class_loader_c = LoadDexInPathClassLoader("MultiDex", class_loader_b);
+ LoadDexInDelegateLastClassLoader("Interfaces", class_loader_c);
+}
+
+class ClassLinkerClassLoaderTest : public ClassLinkerTest {
+ protected:
+ // Verifies that the class identified by the given descriptor is loaded with
+ // the expected_class_loader_obj when search from class_loader_to_search_obj.
+ // When expected_class_loader_obj is null the check will be done against BootClassLoader.
+ void VerifyClassResolution(const std::string& descriptor,
+ jobject class_loader_to_search_obj,
+ jobject expected_class_loader_obj,
+ bool should_find = true) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ StackHandleScope<3> hs(self);
+ Handle<mirror::ClassLoader> class_loader_to_search(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader_to_search_obj)));
+
+ Handle<mirror::Class> klass = hs.NewHandle(
+ class_linker_->FindClass(soa.Self(), descriptor.c_str(), class_loader_to_search));
+
+ if (!should_find) {
+ if (self->IsExceptionPending()) {
+ self->ClearException();
+ }
+ ASSERT_TRUE(klass == nullptr);
+ } else if (expected_class_loader_obj == nullptr) {
+ ASSERT_TRUE(ClassLinker::IsBootClassLoader(soa, klass->GetClassLoader()));
+ } else {
+ ASSERT_TRUE(klass != nullptr) << descriptor;
+ Handle<mirror::ClassLoader> expected_class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(expected_class_loader_obj)));
+ ASSERT_EQ(klass->GetClassLoader(), expected_class_loader.Get());
+ }
+ }
+};
+
+TEST_F(ClassLinkerClassLoaderTest, CreatePathClassLoader) {
+ jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr);
+ VerifyClassResolution("LDefinedInA;", class_loader_a, class_loader_a);
+ VerifyClassResolution("Ljava/lang/String;", class_loader_a, nullptr);
+ VerifyClassResolution("LDefinedInB;", class_loader_a, nullptr, /*should_find*/ false);
+}
+
+TEST_F(ClassLinkerClassLoaderTest, CreateDelegateLastClassLoader) {
+ jobject class_loader_a = LoadDexInDelegateLastClassLoader("ForClassLoaderA", nullptr);
+ VerifyClassResolution("LDefinedInA;", class_loader_a, class_loader_a);
+ VerifyClassResolution("Ljava/lang/String;", class_loader_a, nullptr);
+ VerifyClassResolution("LDefinedInB;", class_loader_a, nullptr, /*should_find*/ false);
+}
+
+TEST_F(ClassLinkerClassLoaderTest, CreateClassLoaderChain) {
+ // The chain is
+ // ClassLoaderA (PathClassLoader, defines: A, AB, AC, AD)
+ // ^
+ // |
+ // ClassLoaderB (DelegateLastClassLoader, defines: B, AB, BC, BD)
+ // ^
+ // |
+ // ClassLoaderC (PathClassLoader, defines: C, AC, BC, CD)
+ // ^
+ // |
+ // ClassLoaderD (DelegateLastClassLoader, defines: D, AD, BD, CD)
+
+ jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr);
+ jobject class_loader_b = LoadDexInDelegateLastClassLoader("ForClassLoaderB", class_loader_a);
+ jobject class_loader_c = LoadDexInPathClassLoader("ForClassLoaderC", class_loader_b);
+ jobject class_loader_d = LoadDexInDelegateLastClassLoader("ForClassLoaderD", class_loader_c);
+
+ // Verify exclusive classes (present in only one class loader).
+ VerifyClassResolution("LDefinedInD;", class_loader_d, class_loader_d);
+ VerifyClassResolution("LDefinedInC;", class_loader_d, class_loader_c);
+ VerifyClassResolution("LDefinedInB;", class_loader_d, class_loader_b);
+ VerifyClassResolution("LDefinedInA;", class_loader_d, class_loader_a);
+
+ // Verify classes that are defined in multiple classloader.
+
+ // Classes defined in B should be found in B even if they are defined in A or C because
+ // B is a DelegateLastClassLoader.
+ VerifyClassResolution("LDefinedInAB;", class_loader_d, class_loader_b);
+ VerifyClassResolution("LDefinedInABC;", class_loader_d, class_loader_b);
+ VerifyClassResolution("LDefinedInBC;", class_loader_d, class_loader_b);
+
+ // Classes defined in D should be found in D even if they are defined in parent class loaders
+ // as well because D is a DelegateLastClassLoader.
+ VerifyClassResolution("LDefinedInAD;", class_loader_d, class_loader_d);
+ VerifyClassResolution("LDefinedInBD;", class_loader_d, class_loader_d);
+ VerifyClassResolution("LDefinedInCD;", class_loader_d, class_loader_d);
+
+
+ // Classes not defined in the DelegateLastClassLoaders (i.e. D or B) should be found
+ // in the top parent.
+ VerifyClassResolution("LDefinedInAC;", class_loader_d, class_loader_a);
+
+ // Sanity check that we don't find an undefined class.
+ VerifyClassResolution("LNotDefined;", class_loader_d, nullptr, /*should_find*/ false);
+}
+
} // namespace art
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 6441a44e6e..659c7e4950 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -680,19 +680,66 @@ jobject CommonRuntimeTestImpl::LoadMultiDex(const char* first_dex_name,
}
jobject CommonRuntimeTestImpl::LoadDex(const char* dex_name) {
- std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name);
+ jobject class_loader = LoadDexInPathClassLoader(dex_name, nullptr);
+ Thread::Current()->SetClassLoaderOverride(class_loader);
+ return class_loader;
+}
+
+jobject CommonRuntimeTestImpl::LoadDexInWellKnownClassLoader(const std::string& dex_name,
+ jclass loader_class,
+ jobject parent_loader) {
+ std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name.c_str());
std::vector<const DexFile*> class_path;
CHECK_NE(0U, dex_files.size());
for (auto& dex_file : dex_files) {
class_path.push_back(dex_file.get());
loaded_dex_files_.push_back(std::move(dex_file));
}
-
Thread* self = Thread::Current();
- jobject class_loader = Runtime::Current()->GetClassLinker()->CreatePathClassLoader(self,
- class_path);
- self->SetClassLoaderOverride(class_loader);
- return class_loader;
+ ScopedObjectAccess soa(self);
+
+ jobject result = Runtime::Current()->GetClassLinker()->CreateWellKnownClassLoader(
+ self,
+ class_path,
+ loader_class,
+ parent_loader);
+
+ {
+ // Verify we build the correct chain.
+
+ ObjPtr<mirror::ClassLoader> actual_class_loader = soa.Decode<mirror::ClassLoader>(result);
+ // Verify that the result has the correct class.
+ CHECK_EQ(soa.Decode<mirror::Class>(loader_class), actual_class_loader->GetClass());
+ // Verify that the parent is not null. The boot class loader will be set up as a
+ // proper object.
+ ObjPtr<mirror::ClassLoader> actual_parent(actual_class_loader->GetParent());
+ CHECK(actual_parent != nullptr);
+
+ if (parent_loader != nullptr) {
+ // We were given a parent. Verify that it's what we expect.
+ ObjPtr<mirror::ClassLoader> expected_parent = soa.Decode<mirror::ClassLoader>(parent_loader);
+ CHECK_EQ(expected_parent, actual_parent);
+ } else {
+ // No parent given. The parent must be the BootClassLoader.
+ CHECK(Runtime::Current()->GetClassLinker()->IsBootClassLoader(soa, actual_parent));
+ }
+ }
+
+ return result;
+}
+
+jobject CommonRuntimeTestImpl::LoadDexInPathClassLoader(const std::string& dex_name,
+ jobject parent_loader) {
+ return LoadDexInWellKnownClassLoader(dex_name,
+ WellKnownClasses::dalvik_system_PathClassLoader,
+ parent_loader);
+}
+
+jobject CommonRuntimeTestImpl::LoadDexInDelegateLastClassLoader(const std::string& dex_name,
+ jobject parent_loader) {
+ return LoadDexInWellKnownClassLoader(dex_name,
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader,
+ parent_loader);
}
std::string CommonRuntimeTestImpl::GetCoreFileLocation(const char* suffix) {
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 3b3e6c5321..5893573bdd 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -134,10 +134,20 @@ class CommonRuntimeTestImpl {
std::unique_ptr<const DexFile> OpenTestDexFile(const char* name);
+ // Loads the test dex file identified by the given dex_name into a PathClassLoader.
+ // Returns the created class loader.
jobject LoadDex(const char* dex_name) REQUIRES_SHARED(Locks::mutator_lock_);
+ // Loads the test dex file identified by the given first_dex_name and second_dex_name
+ // into a PathClassLoader. Returns the created class loader.
jobject LoadMultiDex(const char* first_dex_name, const char* second_dex_name)
REQUIRES_SHARED(Locks::mutator_lock_);
+ jobject LoadDexInPathClassLoader(const std::string& dex_name, jobject parent_loader);
+ jobject LoadDexInDelegateLastClassLoader(const std::string& dex_name, jobject parent_loader);
+ jobject LoadDexInWellKnownClassLoader(const std::string& dex_name,
+ jclass loader_class,
+ jobject parent_loader);
+
std::string android_data_;
std::string dalvik_cache_;
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index c94a8e0f3e..b59511214d 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -18,7 +18,9 @@
#include <sys/uio.h>
+#include <memory>
#include <set>
+#include <vector>
#include "android-base/stringprintf.h"
@@ -26,6 +28,7 @@
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/enums.h"
+#include "base/strlcpy.h"
#include "base/time_utils.h"
#include "class_linker.h"
#include "class_linker-inl.h"
@@ -4926,11 +4929,20 @@ class StringTable {
StringTable() {
}
- void Add(const std::string& str) {
- table_.insert(str);
- }
+ void Add(const char* str, bool copy_string) {
+ if (UNLIKELY(copy_string)) {
+ // Check whether it's already there.
+ if (table_.find(str) != table_.end()) {
+ return;
+ }
- void Add(const char* str) {
+ // Make a copy.
+ size_t str_len = strlen(str);
+ char* copy = new char[str_len + 1];
+ strlcpy(copy, str, str_len + 1);
+ string_backup_.emplace_back(copy);
+ str = copy;
+ }
table_.insert(str);
}
@@ -4947,17 +4959,23 @@ class StringTable {
}
void WriteTo(std::vector<uint8_t>& bytes) const {
- for (const std::string& str : table_) {
- const char* s = str.c_str();
- size_t s_len = CountModifiedUtf8Chars(s);
+ for (const char* str : table_) {
+ size_t s_len = CountModifiedUtf8Chars(str);
std::unique_ptr<uint16_t[]> s_utf16(new uint16_t[s_len]);
- ConvertModifiedUtf8ToUtf16(s_utf16.get(), s);
+ ConvertModifiedUtf8ToUtf16(s_utf16.get(), str);
JDWP::AppendUtf16BE(bytes, s_utf16.get(), s_len);
}
}
private:
- std::set<std::string> table_;
+ struct ConstCharStarComparator {
+ bool operator()(const char *s1, const char *s2) const {
+ return strcmp(s1, s2) < 0;
+ }
+ };
+
+ std::set<const char*, ConstCharStarComparator> table_;
+ std::vector<std::unique_ptr<char[]>> string_backup_;
DISALLOW_COPY_AND_ASSIGN(StringTable);
};
@@ -5044,12 +5062,13 @@ jbyteArray Dbg::GetRecentAllocations() {
count > 0 && it != end; count--, it++) {
const gc::AllocRecord* record = &it->second;
std::string temp;
- class_names.Add(record->GetClassDescriptor(&temp));
+ const char* class_descr = record->GetClassDescriptor(&temp);
+ class_names.Add(class_descr, !temp.empty());
for (size_t i = 0, depth = record->GetDepth(); i < depth; i++) {
ArtMethod* m = record->StackElement(i).GetMethod();
- class_names.Add(m->GetDeclaringClassDescriptor());
- method_names.Add(m->GetName());
- filenames.Add(GetMethodSourceFile(m));
+ class_names.Add(m->GetDeclaringClassDescriptor(), false);
+ method_names.Add(m->GetName(), false);
+ filenames.Add(GetMethodSourceFile(m), false);
}
}
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index b57e2b2b40..0687b753d8 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -951,13 +951,20 @@ static inline bool DoCallCommon(ArtMethod* called_method,
// Test whether to use the interpreter or compiler entrypoint, and save that result to pass to
// PerformCall. A deoptimization could occur at any time, and we shouldn't change which
// entrypoint to use once we start building the shadow frame.
- bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint(
- called_method, called_method->GetEntryPointFromQuickCompiledCode());
+
+ // For unstarted runtimes, always use the interpreter entrypoint. This fixes the case where we are
+ // doing cross compilation. Note that GetEntryPointFromQuickCompiledCode doesn't use the image
+ // pointer size here and this may case an overflow if it is called from the compiler. b/62402160
+ const bool use_interpreter_entrypoint = !Runtime::Current()->IsStarted() ||
+ ClassLinker::ShouldUseInterpreterEntrypoint(
+ called_method,
+ called_method->GetEntryPointFromQuickCompiledCode());
if (LIKELY(code_item != nullptr)) {
// When transitioning to compiled code, space only needs to be reserved for the input registers.
// The rest of the frame gets discarded. This also prevents accessing the called method's code
// item, saving memory by keeping code items of compiled code untouched.
- if (Runtime::Current()->IsStarted() && !use_interpreter_entrypoint) {
+ if (!use_interpreter_entrypoint) {
+ DCHECK(!Runtime::Current()->IsAotCompiler()) << "Compiler should use interpreter entrypoint";
num_regs = number_of_inputs;
} else {
num_regs = code_item->registers_size_;
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 21239fe3de..d3e8798bd6 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -913,12 +913,12 @@ class JvmtiFunctions {
}
static jvmtiError GetBytecodes(jvmtiEnv* env,
- jmethodID method ATTRIBUTE_UNUSED,
- jint* bytecode_count_ptr ATTRIBUTE_UNUSED,
- unsigned char** bytecodes_ptr ATTRIBUTE_UNUSED) {
+ jmethodID method,
+ jint* bytecode_count_ptr,
+ unsigned char** bytecodes_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_get_bytecodes);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetBytecodes(env, method, bytecode_count_ptr, bytecodes_ptr);
}
static jvmtiError IsMethodNative(jvmtiEnv* env, jmethodID method, jboolean* is_native_ptr) {
@@ -1208,6 +1208,23 @@ class JvmtiFunctions {
return error;
}
+ error = add_extension(
+ reinterpret_cast<jvmtiExtensionFunction>(AllocUtil::GetGlobalJvmtiAllocationState),
+ "com.android.art.alloc.get_global_jvmti_allocation_state",
+ "Returns the total amount of memory currently allocated by all jvmtiEnvs through the"
+ " 'Allocate' jvmti function. This does not include any memory that has been deallocated"
+ " through the 'Deallocate' function. This number is approximate and might not correspond"
+ " exactly to the sum of the sizes of all not freed allocations.",
+ 1,
+ { // NOLINT [whitespace/braces] [4]
+ { "currently_allocated", JVMTI_KIND_OUT, JVMTI_TYPE_JLONG, false},
+ },
+ 1,
+ { ERR(NULL_POINTER) });
+ if (error != ERR(NONE)) {
+ return error;
+ }
+
// Copy into output buffer.
*extension_count_ptr = ext_vector.size();
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index 2d5d527e68..c63e50252b 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -216,7 +216,7 @@ const jvmtiCapabilities kPotentialCapabilities = {
.can_tag_objects = 1,
.can_generate_field_modification_events = 1,
.can_generate_field_access_events = 1,
- .can_get_bytecodes = 0,
+ .can_get_bytecodes = 1,
.can_get_synthetic_attribute = 1,
.can_get_owned_monitor_info = 0,
.can_get_current_contended_monitor = 0,
diff --git a/runtime/openjdkjvmti/jvmti_allocator.h b/runtime/openjdkjvmti/jvmti_allocator.h
index 1225c143f9..44b1cb1c20 100644
--- a/runtime/openjdkjvmti/jvmti_allocator.h
+++ b/runtime/openjdkjvmti/jvmti_allocator.h
@@ -36,6 +36,8 @@
#include "base/macros.h"
#include "jvmti.h"
+#include "ti_allocator.h"
+
namespace openjdkjvmti {
template <typename T> class JvmtiAllocator;
@@ -53,6 +55,7 @@ class JvmtiAllocator<void> {
};
explicit JvmtiAllocator(jvmtiEnv* env) : env_(env) {}
+ explicit JvmtiAllocator() : env_(nullptr) {}
template <typename U>
JvmtiAllocator(const JvmtiAllocator<U>& other) // NOLINT, implicit
@@ -89,6 +92,7 @@ class JvmtiAllocator {
};
explicit JvmtiAllocator(jvmtiEnv* env) : env_(env) {}
+ explicit JvmtiAllocator() : env_(nullptr) {}
template <typename U>
JvmtiAllocator(const JvmtiAllocator<U>& other) // NOLINT, implicit
@@ -108,8 +112,8 @@ class JvmtiAllocator {
pointer allocate(size_type n, JvmtiAllocator<void>::pointer hint ATTRIBUTE_UNUSED = nullptr) {
DCHECK_LE(n, max_size());
if (env_ == nullptr) {
- T* result = reinterpret_cast<T*>(malloc(n * sizeof(T)));
- CHECK(result != nullptr || n == 0u); // Abort if malloc() fails.
+ T* result = reinterpret_cast<T*>(AllocUtil::AllocateImpl(n * sizeof(T)));
+ CHECK(result != nullptr || n == 0u); // Abort if AllocateImpl() fails.
return result;
} else {
unsigned char* result;
@@ -120,7 +124,7 @@ class JvmtiAllocator {
}
void deallocate(pointer p, size_type n ATTRIBUTE_UNUSED) {
if (env_ == nullptr) {
- free(p);
+ AllocUtil::DeallocateImpl(reinterpret_cast<unsigned char*>(p));
} else {
jvmtiError dealloc_error = env_->Deallocate(reinterpret_cast<unsigned char*>(p));
CHECK(dealloc_error == JVMTI_ERROR_NONE);
diff --git a/runtime/openjdkjvmti/jvmti_weak_table.h b/runtime/openjdkjvmti/jvmti_weak_table.h
index 01c24b1917..a5175a42ba 100644
--- a/runtime/openjdkjvmti/jvmti_weak_table.h
+++ b/runtime/openjdkjvmti/jvmti_weak_table.h
@@ -40,6 +40,7 @@
#include "gc_root-inl.h"
#include "globals.h"
#include "jvmti.h"
+#include "jvmti_allocator.h"
#include "mirror/object.h"
#include "thread-current-inl.h"
@@ -191,7 +192,7 @@ class JvmtiWeakTable : public art::gc::SystemWeakHolder {
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(allow_disallow_lock_);
- template <typename Storage, class Allocator = std::allocator<T>>
+ template <typename Storage, class Allocator = JvmtiAllocator<T>>
struct ReleasableContainer;
struct HashGcRoot {
@@ -209,10 +210,12 @@ class JvmtiWeakTable : public art::gc::SystemWeakHolder {
}
};
+ using TagAllocator = JvmtiAllocator<std::pair<const art::GcRoot<art::mirror::Object>, T>>;
std::unordered_map<art::GcRoot<art::mirror::Object>,
T,
HashGcRoot,
- EqGcRoot> tagged_objects_
+ EqGcRoot,
+ TagAllocator> tagged_objects_
GUARDED_BY(allow_disallow_lock_)
GUARDED_BY(art::Locks::mutator_lock_);
// To avoid repeatedly scanning the whole table, remember if we did that since the last sweep.
diff --git a/runtime/openjdkjvmti/ti_allocator.cc b/runtime/openjdkjvmti/ti_allocator.cc
index 603a43ffd2..8a0237d6c3 100644
--- a/runtime/openjdkjvmti/ti_allocator.cc
+++ b/runtime/openjdkjvmti/ti_allocator.cc
@@ -31,23 +31,25 @@
#include "ti_allocator.h"
+#include <malloc.h>
+#include <atomic>
+
#include "art_jvmti.h"
-#include "art_method-inl.h"
#include "base/enums.h"
-#include "dex_file_annotations.h"
-#include "events-inl.h"
-#include "jni_internal.h"
-#include "mirror/object_array-inl.h"
-#include "modifiers.h"
-#include "runtime_callbacks.h"
-#include "scoped_thread_state_change-inl.h"
-#include "ScopedLocalRef.h"
-#include "thread-current-inl.h"
-#include "thread_list.h"
-#include "ti_phase.h"
namespace openjdkjvmti {
+std::atomic<jlong> AllocUtil::allocated;
+
+jvmtiError AllocUtil::GetGlobalJvmtiAllocationState(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jlong* allocated_ptr) {
+ if (allocated_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+ *allocated_ptr = allocated.load();
+ return OK;
+}
+
jvmtiError AllocUtil::Allocate(jvmtiEnv* env ATTRIBUTE_UNUSED,
jlong size,
unsigned char** mem_ptr) {
@@ -57,15 +59,31 @@ jvmtiError AllocUtil::Allocate(jvmtiEnv* env ATTRIBUTE_UNUSED,
*mem_ptr = nullptr;
return OK;
}
- *mem_ptr = static_cast<unsigned char*>(malloc(size));
- return (*mem_ptr != nullptr) ? OK : ERR(OUT_OF_MEMORY);
+ *mem_ptr = AllocateImpl(size);
+ if (UNLIKELY(*mem_ptr == nullptr)) {
+ return ERR(OUT_OF_MEMORY);
+ }
+ return OK;
+}
+
+unsigned char* AllocUtil::AllocateImpl(jlong size) {
+ unsigned char* ret = size != 0 ? reinterpret_cast<unsigned char*>(malloc(size)) : nullptr;
+ if (LIKELY(ret != nullptr)) {
+ allocated += malloc_usable_size(ret);
+ }
+ return ret;
}
jvmtiError AllocUtil::Deallocate(jvmtiEnv* env ATTRIBUTE_UNUSED, unsigned char* mem) {
+ DeallocateImpl(mem);
+ return OK;
+}
+
+void AllocUtil::DeallocateImpl(unsigned char* mem) {
if (mem != nullptr) {
+ allocated -= malloc_usable_size(mem);
free(mem);
}
- return OK;
}
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_allocator.h b/runtime/openjdkjvmti/ti_allocator.h
index 7f1aa6d9c0..35575c3884 100644
--- a/runtime/openjdkjvmti/ti_allocator.h
+++ b/runtime/openjdkjvmti/ti_allocator.h
@@ -35,12 +35,28 @@
#include "jni.h"
#include "jvmti.h"
+#include <atomic>
+#include <memory>
+
namespace openjdkjvmti {
+template<typename T>
+class JvmtiAllocator;
+
class AllocUtil {
public:
static jvmtiError Allocate(jvmtiEnv* env, jlong size, unsigned char** mem_ptr);
static jvmtiError Deallocate(jvmtiEnv* env, unsigned char* mem);
+ static jvmtiError GetGlobalJvmtiAllocationState(jvmtiEnv* env, jlong* total_allocated);
+
+ private:
+ static void DeallocateImpl(unsigned char* mem);
+ static unsigned char* AllocateImpl(jlong size);
+
+ static std::atomic<jlong> allocated;
+
+ template <typename T>
+ friend class JvmtiAllocator;
};
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index beb639e208..9b5b964a4d 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -91,6 +91,40 @@ void MethodUtil::Unregister() {
runtime->GetRuntimeCallbacks()->RemoveMethodCallback(&gMethodCallback);
}
+jvmtiError MethodUtil::GetBytecodes(jvmtiEnv* env,
+ jmethodID method,
+ jint* size_ptr,
+ unsigned char** bytecode_ptr) {
+ if (method == nullptr) {
+ return ERR(INVALID_METHODID);
+ }
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+
+ if (art_method->IsNative()) {
+ return ERR(NATIVE_METHOD);
+ }
+
+ if (size_ptr == nullptr || bytecode_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ const art::DexFile::CodeItem* code_item = art_method->GetCodeItem();
+ if (code_item == nullptr) {
+ *size_ptr = 0;
+ *bytecode_ptr = nullptr;
+ return OK;
+ }
+ // 2 bytes per instruction for dex code.
+ *size_ptr = code_item->insns_size_in_code_units_ * 2;
+ jvmtiError err = env->Allocate(*size_ptr, bytecode_ptr);
+ if (err != OK) {
+ return err;
+ }
+ memcpy(*bytecode_ptr, code_item->insns_, *size_ptr);
+ return OK;
+}
+
jvmtiError MethodUtil::GetArgumentsSize(jvmtiEnv* env ATTRIBUTE_UNUSED,
jmethodID method,
jint* size_ptr) {
diff --git a/runtime/openjdkjvmti/ti_method.h b/runtime/openjdkjvmti/ti_method.h
index cc161c8fed..d95a81b63b 100644
--- a/runtime/openjdkjvmti/ti_method.h
+++ b/runtime/openjdkjvmti/ti_method.h
@@ -44,6 +44,11 @@ class MethodUtil {
static void Register(EventHandler* event_handler);
static void Unregister();
+ static jvmtiError GetBytecodes(jvmtiEnv* env,
+ jmethodID method,
+ jint* count_ptr,
+ unsigned char** bytecodes);
+
static jvmtiError GetArgumentsSize(jvmtiEnv* env, jmethodID method, jint* size_ptr);
static jvmtiError GetMaxLocals(jvmtiEnv* env, jmethodID method, jint* max_ptr);
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 24f194b5ee..8d505e2582 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -37,6 +37,7 @@ namespace art {
jclass WellKnownClasses::dalvik_annotation_optimization_CriticalNative;
jclass WellKnownClasses::dalvik_annotation_optimization_FastNative;
jclass WellKnownClasses::dalvik_system_BaseDexClassLoader;
+jclass WellKnownClasses::dalvik_system_DelegateLastClassLoader;
jclass WellKnownClasses::dalvik_system_DexClassLoader;
jclass WellKnownClasses::dalvik_system_DexFile;
jclass WellKnownClasses::dalvik_system_DexPathList;
@@ -270,6 +271,7 @@ void WellKnownClasses::Init(JNIEnv* env) {
CacheClass(env, "dalvik/annotation/optimization/CriticalNative");
dalvik_annotation_optimization_FastNative = CacheClass(env, "dalvik/annotation/optimization/FastNative");
dalvik_system_BaseDexClassLoader = CacheClass(env, "dalvik/system/BaseDexClassLoader");
+ dalvik_system_DelegateLastClassLoader = CacheClass(env, "dalvik/system/DelegateLastClassLoader");
dalvik_system_DexClassLoader = CacheClass(env, "dalvik/system/DexClassLoader");
dalvik_system_DexFile = CacheClass(env, "dalvik/system/DexFile");
dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index c18473197b..c5a16c1c76 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -47,6 +47,7 @@ struct WellKnownClasses {
static jclass dalvik_annotation_optimization_CriticalNative;
static jclass dalvik_annotation_optimization_FastNative;
static jclass dalvik_system_BaseDexClassLoader;
+ static jclass dalvik_system_DelegateLastClassLoader;
static jclass dalvik_system_DexClassLoader;
static jclass dalvik_system_DexFile;
static jclass dalvik_system_DexPathList;
diff --git a/test/098-ddmc/src/Main.java b/test/098-ddmc/src/Main.java
index 72c5a28203..e9a11d76c2 100644
--- a/test/098-ddmc/src/Main.java
+++ b/test/098-ddmc/src/Main.java
@@ -14,8 +14,11 @@
* limitations under the License.
*/
+import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
public class Main {
public static void main(String[] args) throws Exception {
@@ -27,6 +30,8 @@ public class Main {
testRecentAllocationTracking();
}
+ private static ArrayList<Object> staticHolder = new ArrayList<>(100000);
+
private static void testRecentAllocationTracking() throws Exception {
System.out.println("Confirm empty");
Allocations empty = new Allocations(DdmVmInternal.getRecentAllocations());
@@ -44,18 +49,15 @@ public class Main {
System.out.println("Confirm when we overflow, we don't roll over to zero. b/17392248");
final int overflowAllocations = 64 * 1024; // Won't fit in unsigned 16-bit value.
for (int i = 0; i < overflowAllocations; i++) {
- new Object() {
- // Add a finalizer so that the allocation won't be eliminated.
- public void finalize() {
- System.out.print("");
- }
- };
+ allocate(i, 0);
}
Allocations after = new Allocations(DdmVmInternal.getRecentAllocations());
System.out.println("before < overflowAllocations=" + (before.numberOfEntries < overflowAllocations));
System.out.println("after > before=" + (after.numberOfEntries > before.numberOfEntries));
System.out.println("after.numberOfEntries=" + after.numberOfEntries);
+ staticHolder.clear(); // Free the allocated objects.
+
System.out.println("Disable and confirm back to empty");
DdmVmInternal.enableRecentAllocations(false);
System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
@@ -72,7 +74,7 @@ public class Main {
DdmVmInternal.enableRecentAllocations(true);
System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
for (int i = 0; i < 16 * 1024; i++) {
- new String("fnord");
+ staticHolder.add(new String("fnord"));
}
Allocations first = new Allocations(DdmVmInternal.getRecentAllocations());
DdmVmInternal.enableRecentAllocations(true);
@@ -86,6 +88,50 @@ public class Main {
System.out.println("goodbye=" + goodbye);
}
+ // Allocate a simple object. Use depth for a reasonably deep stack.
+ private static final int ALLOCATE1_DEPTH = 50;
+
+ private static Object createProxy() {
+ try {
+ InvocationHandler handler = new InvocationHandler() {
+ public Object invoke(Object proxy, Method method, Object[] args) {
+ // Don't expect to be invoked.
+ return null;
+ }
+ };
+ return Proxy.newProxyInstance(Main.class.getClassLoader(),
+ new Class[] { Runnable.class }, handler);
+ } catch (Exception e) {
+ // We don't really expect exceptions here.
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static void allocate(int i, int depth) {
+ if (depth >= ALLOCATE1_DEPTH) {
+ // Mix proxies, int arrays and Objects to test the different descriptor paths.
+ switch (i) {
+ case 0:
+ staticHolder.add(createProxy());
+ break;
+
+ case 1:
+ staticHolder.add(new int[0]);
+ break;
+
+ case 2:
+ staticHolder.add(new Object[0]);
+ break;
+
+ default:
+ staticHolder.add(new Object());
+ break;
+ }
+ } else {
+ allocate(i, depth + 1);
+ }
+ }
+
private static class Allocations {
final int messageHeaderLen;
final int entryHeaderLen;
diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java
index 9072c8b538..3cfe0064fc 100644
--- a/test/141-class-unload/src/Main.java
+++ b/test/141-class-unload/src/Main.java
@@ -65,7 +65,8 @@ public class Main {
String line;
int count = 0;
while ((line = reader.readLine()) != null) {
- if (line.contains("@141-class-unload-ex.jar")) {
+ if (line.contains("141-class-unload-ex.odex") ||
+ line.contains("141-class-unload-ex.vdex")) {
System.out.println(line);
++count;
}
diff --git a/test/1900-track-alloc/alloc.cc b/test/1900-track-alloc/alloc.cc
new file mode 100644
index 0000000000..db5617c54c
--- /dev/null
+++ b/test/1900-track-alloc/alloc.cc
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jvmti.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1900TrackAlloc {
+
+typedef jvmtiError (*GetGlobalState)(jvmtiEnv* env, jlong* allocated);
+
+struct AllocTrackingData {
+ GetGlobalState get_global_state;
+};
+
+template <typename T>
+static void Dealloc(T* t) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(t));
+}
+
+template <typename T, typename ...Rest>
+static void Dealloc(T* t, Rest... rs) {
+ Dealloc(t);
+ Dealloc(rs...);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1900_doDeallocate(JNIEnv* env,
+ jclass,
+ jlong jvmti_env_ptr,
+ jlong ptr) {
+ JvmtiErrorToException(env,
+ reinterpret_cast<jvmtiEnv*>(jvmti_env_ptr),
+ reinterpret_cast<jvmtiEnv*>(jvmti_env_ptr)->Deallocate(
+ reinterpret_cast<unsigned char*>(static_cast<intptr_t>(ptr))));
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_art_Test1900_doAllocate(JNIEnv* env,
+ jclass,
+ jlong jvmti_env_ptr,
+ jlong size) {
+ unsigned char* res = nullptr;
+ JvmtiErrorToException(env,
+ reinterpret_cast<jvmtiEnv*>(jvmti_env_ptr),
+ reinterpret_cast<jvmtiEnv*>(jvmti_env_ptr)->Allocate(size, &res));
+ return static_cast<jlong>(reinterpret_cast<intptr_t>(res));
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_art_Test1900_getAmountAllocated(JNIEnv* env, jclass) {
+ AllocTrackingData* data = nullptr;
+ if (JvmtiErrorToException(
+ env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return -1;
+ }
+ if (data == nullptr || data->get_global_state == nullptr) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Alloc tracking data not initialized.");
+ return -1;
+ }
+ jlong allocated = -1;
+ JvmtiErrorToException(env, jvmti_env, data->get_global_state(jvmti_env, &allocated));
+ return allocated;
+}
+
+static void DeallocParams(jvmtiParamInfo* params, jint n_params) {
+ for (jint i = 0; i < n_params; i++) {
+ Dealloc(params[i].name);
+ }
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_art_Test1900_getDefaultJvmtiEnv(JNIEnv*, jclass) {
+ return static_cast<jlong>(reinterpret_cast<intptr_t>(jvmti_env));
+}
+
+extern "C" JNIEXPORT void Java_art_Test1900_destroyJvmtiEnv(JNIEnv* env,
+ jclass,
+ jlong jvmti_env_ptr) {
+ JvmtiErrorToException(env,
+ jvmti_env,
+ reinterpret_cast<jvmtiEnv*>(jvmti_env_ptr)->DisposeEnvironment());
+}
+
+extern "C" JNIEXPORT jlong Java_art_Test1900_newJvmtiEnv(JNIEnv* env, jclass) {
+ JavaVM* vm = nullptr;
+ if (env->GetJavaVM(&vm) != 0) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Unable to get JavaVM");
+ return -1;
+ }
+ jvmtiEnv* new_env = nullptr;
+ if (vm->GetEnv(reinterpret_cast<void**>(&new_env), JVMTI_VERSION_1_0) != 0) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Unable to create new jvmtiEnv");
+ return -1;
+ }
+ return static_cast<jlong>(reinterpret_cast<intptr_t>(new_env));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1900_initializeTest(JNIEnv* env, jclass) {
+ void* old_data = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) {
+ return;
+ } else if (old_data != nullptr) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
+ return;
+ }
+ AllocTrackingData* data = nullptr;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->Allocate(sizeof(AllocTrackingData),
+ reinterpret_cast<unsigned char**>(&data)))) {
+ return;
+ }
+ memset(data, 0, sizeof(AllocTrackingData));
+ // Get the extensions.
+ jint n_ext = 0;
+ jvmtiExtensionFunctionInfo* infos = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetExtensionFunctions(&n_ext, &infos))) {
+ return;
+ }
+ for (jint i = 0; i < n_ext; i++) {
+ jvmtiExtensionFunctionInfo* cur_info = &infos[i];
+ if (strcmp("com.android.art.alloc.get_global_jvmti_allocation_state", cur_info->id) == 0) {
+ data->get_global_state = reinterpret_cast<GetGlobalState>(cur_info->func);
+ }
+ // Cleanup the cur_info
+ DeallocParams(cur_info->params, cur_info->param_count);
+ Dealloc(cur_info->id, cur_info->short_description, cur_info->params, cur_info->errors);
+ }
+ // Cleanup the array.
+ Dealloc(infos);
+ if (data->get_global_state == nullptr) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Unable to find memory tracking extensions.");
+ return;
+ }
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data));
+ return;
+}
+
+} // namespace Test1900TrackAlloc
+} // namespace art
diff --git a/test/1900-track-alloc/expected.txt b/test/1900-track-alloc/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/1900-track-alloc/expected.txt
diff --git a/test/1900-track-alloc/info.txt b/test/1900-track-alloc/info.txt
new file mode 100644
index 0000000000..e1d35ae026
--- /dev/null
+++ b/test/1900-track-alloc/info.txt
@@ -0,0 +1 @@
+Tests the jvmti-extension to get allocated memory snapshot.
diff --git a/test/1900-track-alloc/run b/test/1900-track-alloc/run
new file mode 100755
index 0000000000..c6e62ae6cd
--- /dev/null
+++ b/test/1900-track-alloc/run
@@ -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-run "$@" --jvmti
diff --git a/test/1900-track-alloc/src/Main.java b/test/1900-track-alloc/src/Main.java
new file mode 100644
index 0000000000..0dab4ef726
--- /dev/null
+++ b/test/1900-track-alloc/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1900.run();
+ }
+}
diff --git a/test/1900-track-alloc/src/art/Main.java b/test/1900-track-alloc/src/art/Main.java
new file mode 100644
index 0000000000..aa5498bd62
--- /dev/null
+++ b/test/1900-track-alloc/src/art/Main.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+// Binder class so the agent's C code has something that can be bound and exposed to tests.
+// In a package to separate cleanly and work around CTS reference issues (though this class
+// should be replaced in the CTS version).
+public class Main {
+ // Load the given class with the given classloader, and bind all native methods to corresponding
+ // C methods in the agent. Will abort if any of the steps fail.
+ public static native void bindAgentJNI(String className, ClassLoader classLoader);
+ // Same as above, giving the class directly.
+ public static native void bindAgentJNIForClass(Class<?> klass);
+
+ // Common infrastructure.
+ public static native void setTag(Object o, long tag);
+ public static native long getTag(Object o);
+}
diff --git a/test/1900-track-alloc/src/art/Test1900.java b/test/1900-track-alloc/src/art/Test1900.java
new file mode 100644
index 0000000000..becee1b15c
--- /dev/null
+++ b/test/1900-track-alloc/src/art/Test1900.java
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
+
+public class Test1900 {
+ public static void checkLE(long exp, long o) {
+ if (exp > o) {
+ throw new Error("Expected: " + exp + " Got: " + o);
+ }
+ }
+ public static void checkEq(long exp, long o) {
+ if (exp != o) {
+ throw new Error("Expected: " + exp + " Got: " + o);
+ }
+ }
+
+ public static void runConcurrent(Runnable... rs) throws Exception {
+ final CountDownLatch latch = new CountDownLatch(rs.length);
+ Thread[] thrs = new Thread[rs.length];
+ for (int i = 0; i < rs.length; i++) {
+ final Runnable r = rs[i];
+ thrs[i] = new Thread(() -> {
+ latch.countDown();
+ r.run();
+ });
+ thrs[i].start();
+ }
+ for (Thread thr : thrs) {
+ thr.join();
+ }
+ }
+ static class Holder {
+ public long val;
+ }
+
+ public static void run() throws Exception {
+ initializeTest();
+ // Get the overhead for the native part of this test.
+ final long base_state = getAmountAllocated();
+
+ // Basic alloc-dealloc
+ checkEq(base_state + 0, getAmountAllocated());
+ long abc = doAllocate(10);
+ checkLE(base_state + 10, getAmountAllocated());
+ long def = doAllocate(10);
+ checkLE(base_state + 20, getAmountAllocated());
+ doDeallocate(abc);
+ checkLE(base_state + 10, getAmountAllocated());
+
+ doDeallocate(def);
+
+ checkEq(base_state + 0, getAmountAllocated());
+
+ // Try doing it concurrently.
+ Runnable add10 = () -> { long x = doAllocate(10); doDeallocate(x); };
+ Runnable[] rs = new Runnable[100];
+ Arrays.fill(rs, add10);
+ runConcurrent(rs);
+ checkEq(base_state + 0, getAmountAllocated());
+
+ // Try doing it concurrently with different threads to allocate and deallocate.
+ final Semaphore sem = new Semaphore(0);
+ final Holder h = new Holder();
+ runConcurrent(
+ () -> {
+ try {
+ h.val = doAllocate(100);
+ checkLE(base_state + 100, getAmountAllocated());
+ sem.release();
+ } catch (Exception e) { throw new Error("exception!", e); }
+ },
+ () -> {
+ try {
+ sem.acquire();
+ long after_acq = getAmountAllocated();
+ doDeallocate(h.val);
+ checkLE(base_state + 100, after_acq);
+ } catch (Exception e) { throw new Error("exception!", e); }
+ }
+ );
+ checkEq(base_state + 0, getAmountAllocated());
+
+ // Try doing it with multiple jvmtienvs.
+ long env1 = newJvmtiEnv();
+ long env2 = newJvmtiEnv();
+
+ final long new_base_state = getAmountAllocated();
+ // new jvmtienvs shouldn't save us memory.
+ checkLE(base_state, new_base_state);
+ // Make sure we track both.
+ abc = doAllocate(env1, 10);
+ checkLE(new_base_state + 10, getAmountAllocated());
+ def = doAllocate(env2, 10);
+ checkLE(new_base_state + 20, getAmountAllocated());
+ doDeallocate(env1, abc);
+ checkLE(new_base_state + 10, getAmountAllocated());
+
+ doDeallocate(env2, def);
+
+ checkEq(new_base_state + 0, getAmountAllocated());
+
+ destroyJvmtiEnv(env1);
+ destroyJvmtiEnv(env2);
+
+ // Back to normal after getting rid of the envs.
+ checkEq(base_state + 0, getAmountAllocated());
+
+ // Try adding some tags
+ Object a = new Object();
+ Object b = new Object();
+ Main.setTag(a, 100);
+ Main.setTag(b, 200);
+
+ // tags should be counted and should have some data associated with them.
+ checkLE(base_state + 1, getAmountAllocated());
+ }
+
+ private static native long doAllocate(long jvmtienv, long size);
+ private static long doAllocate(long size) {
+ return doAllocate(getDefaultJvmtiEnv(), size);
+ }
+
+ private static native void doDeallocate(long jvmtienv, long ptr);
+ private static void doDeallocate(long size) {
+ doDeallocate(getDefaultJvmtiEnv(), size);
+ }
+
+ private static native long getDefaultJvmtiEnv();
+ private static native long newJvmtiEnv();
+ private static native void destroyJvmtiEnv(long jvmtienv);
+ private static native long getAmountAllocated();
+ private static native void initializeTest();
+}
diff --git a/test/1901-get-bytecodes/bytecodes.cc b/test/1901-get-bytecodes/bytecodes.cc
new file mode 100644
index 0000000000..edcb734788
--- /dev/null
+++ b/test/1901-get-bytecodes/bytecodes.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+#include <pthread.h>
+#include <stdio.h>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "jni.h"
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
+
+#include "jvmti.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1901Bytecodes {
+
+extern "C" JNIEXPORT jbyteArray JNICALL Java_art_Test1901_getBytecodes(JNIEnv* env,
+ jclass,
+ jobject jmethod) {
+ jmethodID method = env->FromReflectedMethod(jmethod);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ unsigned char* bytecodes = nullptr;
+ jint bytecodes_size = 0;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetBytecodes(method, &bytecodes_size, &bytecodes))) {
+ return nullptr;
+ }
+ jbyteArray out = env->NewByteArray(bytecodes_size);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ } else if (bytecodes_size == 0) {
+ return out;
+ }
+ jbyte* bytes = env->GetByteArrayElements(out, /* is_copy */ nullptr);
+ memcpy(bytes, bytecodes, bytecodes_size);
+ env->ReleaseByteArrayElements(out, bytes, 0);
+ return out;
+}
+
+} // namespace Test1901Bytecodes
+} // namespace art
diff --git a/test/1901-get-bytecodes/expected.txt b/test/1901-get-bytecodes/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/1901-get-bytecodes/expected.txt
diff --git a/test/1901-get-bytecodes/info.txt b/test/1901-get-bytecodes/info.txt
new file mode 100644
index 0000000000..c8c91893e5
--- /dev/null
+++ b/test/1901-get-bytecodes/info.txt
@@ -0,0 +1,3 @@
+Tests basic functions in the jvmti plugin.
+
+Tests that the GetBytecodes function works as expected.
diff --git a/test/1901-get-bytecodes/run b/test/1901-get-bytecodes/run
new file mode 100755
index 0000000000..c6e62ae6cd
--- /dev/null
+++ b/test/1901-get-bytecodes/run
@@ -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-run "$@" --jvmti
diff --git a/test/1901-get-bytecodes/src/Main.java b/test/1901-get-bytecodes/src/Main.java
new file mode 100644
index 0000000000..d37fcdb0cb
--- /dev/null
+++ b/test/1901-get-bytecodes/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1901.run();
+ }
+}
diff --git a/test/1901-get-bytecodes/src/art/Test1901.java b/test/1901-get-bytecodes/src/art/Test1901.java
new file mode 100644
index 0000000000..6940491e3c
--- /dev/null
+++ b/test/1901-get-bytecodes/src/art/Test1901.java
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Base64;
+
+public class Test1901 {
+ // Class & Dex file containing the following class.
+ // Using this representation to prevent any changes to the compiler or the file formats from
+ // changing the output of this test.
+ //
+ // package art;
+ // public class Target {
+ // public void doNothing() {
+ // return;
+ // }
+ //
+ // public static void staticNothing() {
+ // return;
+ // }
+ //
+ // public void sayHi() {
+ // System.out.println("hello");
+ // }
+ // }
+ public static byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAHgoABgAQCQARABIIABMKABQAFQcAFgcAFwEABjxpbml0PgEAAygpVgEABENvZGUB" +
+ "AA9MaW5lTnVtYmVyVGFibGUBAAlkb05vdGhpbmcBAA1zdGF0aWNOb3RoaW5nAQAFc2F5SGkBAApT" +
+ "b3VyY2VGaWxlAQALVGFyZ2V0LmphdmEMAAcACAcAGAwAGQAaAQAFaGVsbG8HABsMABwAHQEACmFy" +
+ "dC9UYXJnZXQBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxq" +
+ "YXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExq" +
+ "YXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAAEAAEABwAIAAEACQAAAB0AAQABAAAABSq3AAGx" +
+ "AAAAAQAKAAAABgABAAAAAgABAAsACAABAAkAAAAZAAAAAQAAAAGxAAAAAQAKAAAABgABAAAABAAJ" +
+ "AAwACAABAAkAAAAZAAAAAAAAAAGxAAAAAQAKAAAABgABAAAACAABAA0ACAABAAkAAAAlAAIAAQAA" +
+ "AAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAMAAgADQABAA4AAAACAA8=");
+ public static byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAbYkxNjiZ8a+fNWF4smR2+uXbrq88/FNoYAwAAcAAAAHhWNBIAAAAAAAAAAHgCAAAP" +
+ "AAAAcAAAAAYAAACsAAAAAgAAAMQAAAABAAAA3AAAAAYAAADkAAAAAQAAABQBAADkAQAANAEAAJoB" +
+ "AACiAQAAsAEAAMcBAADbAQAA7wEAAAMCAAAQAgAAEwIAABcCAAAiAgAAKQIAAC4CAAA3AgAAPgIA" +
+ "AAEAAAACAAAAAwAAAAQAAAAFAAAABwAAAAcAAAAFAAAAAAAAAAgAAAAFAAAAlAEAAAQAAQALAAAA" +
+ "AAAAAAAAAAAAAAAACQAAAAAAAAANAAAAAAAAAA4AAAABAAEADAAAAAIAAAAAAAAAAAAAAAEAAAAC" +
+ "AAAAAAAAAAYAAAAAAAAAYgIAAAAAAAABAAEAAQAAAE0CAAAEAAAAcBAFAAAADgAAAAAAAAAAAFIC" +
+ "AAABAAAADgAAAAEAAQAAAAAAVwIAAAEAAAAOAAAAAwABAAIAAABcAgAACAAAAGIAAAAaAQoAbiAE" +
+ "ABAADgABAAAAAwAGPGluaXQ+AAxMYXJ0L1RhcmdldDsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwAS" +
+ "TGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVt" +
+ "OwALVGFyZ2V0LmphdmEAAVYAAlZMAAlkb05vdGhpbmcABWhlbGxvAANvdXQAB3ByaW50bG4ABXNh" +
+ "eUhpAA1zdGF0aWNOb3RoaW5nAAIABw4ACAAHDgAEAAcOAAwABw54AAAAAgIAgYAEtAIDCcwCAQHg" +
+ "AgEB9AINAAAAAAAAAAEAAAAAAAAAAQAAAA8AAABwAAAAAgAAAAYAAACsAAAAAwAAAAIAAADEAAAA" +
+ "BAAAAAEAAADcAAAABQAAAAYAAADkAAAABgAAAAEAAAAUAQAAASAAAAQAAAA0AQAAARAAAAEAAACU" +
+ "AQAAAiAAAA8AAACaAQAAAyAAAAQAAABNAgAAACAAAAEAAABiAgAAABAAAAEAAAB4AgAA");
+
+ public static byte[][] DO_NOTHING_BYTECODES = new byte[][] {
+ // Dex Bytecodes for doNothing
+ // 0e00 |0000: return-void
+ new byte[] { 14, 0 },
+ // Java bytecodes
+ // 0: return
+ new byte[] { -79 },
+ };
+
+ public static byte[][] STATIC_NOTHING_BYTECODES = new byte[][] {
+ // Dex Bytecodes for staticNothing
+ // 0e00 |0000: return-void
+ new byte[] { 14, 0 },
+ // Java bytecodes
+ // 0: return
+ new byte[] { -79 },
+ };
+
+ public static byte[][] SAY_HI_NOTHING_BYTECODES = new byte[][] {
+ // Dex Bytecodes for sayHi
+ // 6200 0000 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0000
+ // 1a01 0a00 |0002: const-string v1, "hello" // string@000a
+ // 6e20 0400 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0004
+ // 0e00 |0007: return-void
+ new byte[] { 98, 0, 0, 0, 26, 1, 10, 0, 110, 32, 4, 0, 16, 0, 14, 0 },
+ // Java bytecodes
+ // 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
+ // 3: ldc #3 // String hello
+ // 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
+ // 8: return
+ new byte[] { -78, 0, 2, 18, 3, -74, 0, 4, -79 },
+ };
+
+ public static ClassLoader getClassLoader() throws Exception {
+ try {
+ Class<?> class_loader_class = Class.forName("dalvik.system.InMemoryDexClassLoader");
+ Constructor<?> ctor = class_loader_class.getConstructor(ByteBuffer.class, ClassLoader.class);
+ // We are on art since we got the InMemoryDexClassLoader.
+ return (ClassLoader)ctor.newInstance(
+ ByteBuffer.wrap(DEX_BYTES), Test1901.class.getClassLoader());
+ } catch (ClassNotFoundException e) {
+ // Running on RI.
+ return new ClassLoader(Test1901.class.getClassLoader()) {
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ if (name.equals("art.Target")) {
+ return defineClass(name, CLASS_BYTES, 0, CLASS_BYTES.length);
+ } else {
+ return super.findClass(name);
+ }
+ }
+ };
+ }
+ }
+
+ public static void CheckMethodBytes(Method m, byte[][] possible_bytecodes) {
+ byte[] real_codes = getBytecodes(m);
+ for (byte[] pos : possible_bytecodes) {
+ if (Arrays.equals(pos, real_codes)) {
+ return;
+ }
+ }
+ System.out.println("Unexpected bytecodes for " + m);
+ System.out.println("Received: " + Arrays.toString(real_codes));
+ System.out.println("Expected one of:");
+ for (byte[] pos : possible_bytecodes) {
+ System.out.println("\t" + Arrays.toString(pos));
+ }
+ }
+
+ public static void run() throws Exception {
+ Class<?> target = getClassLoader().loadClass("art.Target");
+ CheckMethodBytes(target.getDeclaredMethod("doNothing"), DO_NOTHING_BYTECODES);
+ CheckMethodBytes(target.getDeclaredMethod("staticNothing"), STATIC_NOTHING_BYTECODES);
+ CheckMethodBytes(target.getDeclaredMethod("sayHi"), SAY_HI_NOTHING_BYTECODES);
+ }
+
+ public static native byte[] getBytecodes(Method m);
+}
diff --git a/test/596-app-images/src/Main.java b/test/596-app-images/src/Main.java
index 8ee3c888b0..88d95f4162 100644
--- a/test/596-app-images/src/Main.java
+++ b/test/596-app-images/src/Main.java
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+import java.lang.reflect.Field;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
class Main {
static class Inner {
final public static int abc = 10;
@@ -46,13 +50,76 @@ class Main {
if (!checkInitialized(StaticFieldsInit.class))
System.out.println("StaticFieldsInit class is not initialized!");
- if (checkInitialized(StaticInternString.class))
- System.out.println("StaticInternString class is initialized!");
+ if (!checkInitialized(StaticInternString.class))
+ System.out.println("StaticInternString class is not initialized!");
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("java.");
+ sb.append("abc.");
+ sb.append("Action");
+
+ String tmp = sb.toString();
+ String intern = tmp.intern();
+
+ assertNotEqual(tmp, intern, "Dynamically constructed String, not interned.");
+ assertEqual(intern, StaticInternString.intent, "Static encoded literal String not interned.");
+ assertEqual(BootInternedString.boot, BootInternedString.boot.intern(),
+ "Static encoded literal String not moved back to runtime intern table.");
+
+ try {
+ Field f = StaticInternString.class.getDeclaredField("intent");
+ assertEqual(intern, f.get(null), "String Literals are not interned properly.");
+
+ } catch (Exception e) {
+ System.out.println("Exception");
+ }
+
+ assertEqual(StaticInternString.getIntent(), StaticInternString2.getIntent(),
+ "String Literals are not intenred properly, App image static strings duplicated.");
+
+ // reload the class StaticInternString, check whether static strings interned properly
+ final String DEX_FILE = System.getenv("DEX_LOCATION") + "/596-app-images.jar";
+ final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path");
+
+ try {
+ Class<?> pathClassLoader = Class.forName("dalvik.system.PathClassLoader");
+ if (pathClassLoader == null) {
+ throw new AssertionError("Counldn't find path class loader class");
+ }
+ Constructor<?> ctor =
+ pathClassLoader.getDeclaredConstructor(String.class, String.class, ClassLoader.class);
+ ClassLoader loader = (ClassLoader) ctor.newInstance(
+ DEX_FILE, LIBRARY_SEARCH_PATH, null);
+
+ Class<?> staticInternString = loader.loadClass("StaticInternString");
+
+ if (!checkAppImageContains(staticInternString)) {
+ System.out.println("Not loaded again.");
+ }
+ Method getIntent = staticInternString.getDeclaredMethod("getIntent");
+
+ assertEqual(StaticInternString.getIntent(), getIntent.invoke(staticInternString),
+ "Dynamically loaded app image's literal strings not interned properly.");
+ } catch (Exception e) {
+ e.printStackTrace(System.out);
+ }
+
}
public static native boolean checkAppImageLoaded();
public static native boolean checkAppImageContains(Class<?> klass);
public static native boolean checkInitialized(Class<?> klass);
+
+ public static void assertEqual(Object a, Object b, String msg) {
+ if (a != b)
+ System.out.println(msg);
+ }
+
+ public static void assertNotEqual(Object a, Object b, String msg) {
+ if (a == b)
+ System.out.println(msg);
+ }
+
}
class StaticFields{
@@ -68,6 +135,21 @@ class StaticFieldsInit{
}
class StaticInternString {
- final public static String intern = "java.abc.Action";
+ final public static String intent = "java.abc.Action";
+ static public String getIntent() {
+ return intent;
+ }
+}
+
+class BootInternedString {
+ final public static String boot = "double";
+}
+
+class StaticInternString2 {
+ final public static String intent = "java.abc.Action";
+
+ static String getIntent() {
+ return intent;
+ }
}
diff --git a/test/988-method-trace/check b/test/988-method-trace/check
new file mode 100644
index 0000000000..4c583eb051
--- /dev/null
+++ b/test/988-method-trace/check
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# 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.
+
+# Jack uses @hide API which gives it wrong method trace in the expected.txt
+if [[ "$USE_JACK" == true ]]; then
+ patch -p0 expected.txt < expected_jack.diff >/dev/null
+fi
+
+./default-check "$@"
diff --git a/test/988-method-trace/expected.txt b/test/988-method-trace/expected.txt
index 8e42a48865..574d5b0772 100644
--- a/test/988-method-trace/expected.txt
+++ b/test/988-method-trace/expected.txt
@@ -1,4 +1,4 @@
-.<= public static native void art.Trace.enableTracing(java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.Thread) -> <null: null>
+.<= public static void art.Trace.enableTracing(java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.Thread) -> <null: null>
<= public static void art.Trace.enableMethodTracing(java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.Thread) -> <null: null>
=> art.Test988$IterOp()
.=> public java.lang.Object()
@@ -130,8 +130,8 @@ fibonacci(5)=5
....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...<= public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...=> public java.lang.String java.lang.StringBuilder.toString()
-....=> static native java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[])
-....<= static native java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> <class java.lang.String: Bad argument: -19 < 0>
+....=> static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[])
+....<= static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> <class java.lang.String: Bad argument: -19 < 0>
...<= public java.lang.String java.lang.StringBuilder.toString() -> <class java.lang.String: Bad argument: -19 < 0>
...=> public java.lang.Error(java.lang.String)
....=> public java.lang.Throwable(java.lang.String)
@@ -140,13 +140,13 @@ fibonacci(5)=5
.....=> public static final java.util.List java.util.Collections.emptyList()
.....<= public static final java.util.List java.util.Collections.emptyList() -> <class java.util.Collections$EmptyList: []>
.....=> public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace()
-......=> private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
-......<= private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
+......=> private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
+......<= private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
.....<= public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace() -> <class java.lang.Error: java.lang.Error: Bad argument: -19 < 0
- art.Test988.iter_fibonacci(Test988.java:228)
- art.Test988$IterOp.applyAsInt(Test988.java:223)
- art.Test988.doFibTest(Test988.java:316)
- art.Test988.run(Test988.java:286)
+ art.Test988.iter_fibonacci(Test988.java:235)
+ art.Test988$IterOp.applyAsInt(Test988.java:230)
+ art.Test988.doFibTest(Test988.java:339)
+ art.Test988.run(Test988.java:304)
<additional hidden frames>
>
....<= public java.lang.Throwable(java.lang.String) -> <null: null>
@@ -163,10 +163,10 @@ fibonacci(5)=5
...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
- art.Test988.iter_fibonacci(Test988.java:228)
- art.Test988$IterOp.applyAsInt(Test988.java:223)
- art.Test988.doFibTest(Test988.java:316)
- art.Test988.run(Test988.java:286)
+ art.Test988.iter_fibonacci(Test988.java:235)
+ art.Test988$IterOp.applyAsInt(Test988.java:230)
+ art.Test988.doFibTest(Test988.java:339)
+ art.Test988.run(Test988.java:304)
<additional hidden frames>
.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
@@ -231,8 +231,8 @@ fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...<= public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...=> public java.lang.String java.lang.StringBuilder.toString()
-....=> static native java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[])
-....<= static native java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> <class java.lang.String: Bad argument: -19 < 0>
+....=> static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[])
+....<= static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> <class java.lang.String: Bad argument: -19 < 0>
...<= public java.lang.String java.lang.StringBuilder.toString() -> <class java.lang.String: Bad argument: -19 < 0>
...=> public java.lang.Error(java.lang.String)
....=> public java.lang.Throwable(java.lang.String)
@@ -241,13 +241,13 @@ fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
.....=> public static final java.util.List java.util.Collections.emptyList()
.....<= public static final java.util.List java.util.Collections.emptyList() -> <class java.util.Collections$EmptyList: []>
.....=> public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace()
-......=> private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
-......<= private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
+......=> private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
+......<= private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
.....<= public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace() -> <class java.lang.Error: java.lang.Error: Bad argument: -19 < 0
- art.Test988.fibonacci(Test988.java:250)
- art.Test988$RecurOp.applyAsInt(Test988.java:245)
- art.Test988.doFibTest(Test988.java:316)
- art.Test988.run(Test988.java:287)
+ art.Test988.fibonacci(Test988.java:257)
+ art.Test988$RecurOp.applyAsInt(Test988.java:252)
+ art.Test988.doFibTest(Test988.java:339)
+ art.Test988.run(Test988.java:305)
<additional hidden frames>
>
....<= public java.lang.Throwable(java.lang.String) -> <null: null>
@@ -264,14 +264,194 @@ fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
- art.Test988.fibonacci(Test988.java:250)
- art.Test988$RecurOp.applyAsInt(Test988.java:245)
- art.Test988.doFibTest(Test988.java:316)
- art.Test988.run(Test988.java:287)
+ art.Test988.fibonacci(Test988.java:257)
+ art.Test988$RecurOp.applyAsInt(Test988.java:252)
+ art.Test988.doFibTest(Test988.java:339)
+ art.Test988.run(Test988.java:305)
<additional hidden frames>
.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
-=> public static native java.lang.Thread java.lang.Thread.currentThread()
-<= public static native java.lang.Thread java.lang.Thread.currentThread() -> <<non-deterministic>: <non-deterministic>>
-=> public static native void art.Trace.disableTracing(java.lang.Thread)
+=> static void art.Test988$IntrinsicsTest.doTest()
+.=> static void art.Test988Intrinsics.test()
+..=> public static long java.lang.Double.doubleToRawLongBits(double)
+..<= public static long java.lang.Double.doubleToRawLongBits(double) -> <class java.lang.Long: 0>
+..=> public static long java.lang.Double.doubleToLongBits(double)
+..<= public static long java.lang.Double.doubleToLongBits(double) -> <class java.lang.Long: 0>
+..=> public static boolean java.lang.Double.isInfinite(double)
+..<= public static boolean java.lang.Double.isInfinite(double) -> <class java.lang.Boolean: false>
+..=> public static boolean java.lang.Double.isNaN(double)
+..<= public static boolean java.lang.Double.isNaN(double) -> <class java.lang.Boolean: false>
+..=> public static double java.lang.Double.longBitsToDouble(long)
+..<= public static double java.lang.Double.longBitsToDouble(long) -> <class java.lang.Double: 0.0>
+..=> public static int java.lang.Float.floatToRawIntBits(float)
+..<= public static int java.lang.Float.floatToRawIntBits(float) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Float.floatToIntBits(float)
+..<= public static int java.lang.Float.floatToIntBits(float) -> <class java.lang.Integer: 0>
+..=> public static boolean java.lang.Float.isInfinite(float)
+..<= public static boolean java.lang.Float.isInfinite(float) -> <class java.lang.Boolean: false>
+..=> public static boolean java.lang.Float.isNaN(float)
+..<= public static boolean java.lang.Float.isNaN(float) -> <class java.lang.Boolean: false>
+..=> public static float java.lang.Float.intBitsToFloat(int)
+..<= public static float java.lang.Float.intBitsToFloat(int) -> <class java.lang.Float: 0.0>
+..=> public static int java.lang.Integer.reverse(int)
+..<= public static int java.lang.Integer.reverse(int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.reverseBytes(int)
+..<= public static int java.lang.Integer.reverseBytes(int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.bitCount(int)
+..<= public static int java.lang.Integer.bitCount(int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.compare(int,int)
+..<= public static int java.lang.Integer.compare(int,int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.highestOneBit(int)
+..<= public static int java.lang.Integer.highestOneBit(int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.lowestOneBit(int)
+..<= public static int java.lang.Integer.lowestOneBit(int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.numberOfLeadingZeros(int)
+..<= public static int java.lang.Integer.numberOfLeadingZeros(int) -> <class java.lang.Integer: 32>
+..=> public static int java.lang.Integer.numberOfTrailingZeros(int)
+..<= public static int java.lang.Integer.numberOfTrailingZeros(int) -> <class java.lang.Integer: 32>
+..=> public static int java.lang.Integer.rotateRight(int,int)
+..<= public static int java.lang.Integer.rotateRight(int,int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.rotateLeft(int,int)
+..<= public static int java.lang.Integer.rotateLeft(int,int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.signum(int)
+..<= public static int java.lang.Integer.signum(int) -> <class java.lang.Integer: 0>
+..=> public static long java.lang.Long.reverse(long)
+..<= public static long java.lang.Long.reverse(long) -> <class java.lang.Long: 0>
+..=> public static long java.lang.Long.reverseBytes(long)
+..<= public static long java.lang.Long.reverseBytes(long) -> <class java.lang.Long: 0>
+..=> public static int java.lang.Long.bitCount(long)
+..<= public static int java.lang.Long.bitCount(long) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Long.compare(long,long)
+..<= public static int java.lang.Long.compare(long,long) -> <class java.lang.Integer: 0>
+..=> public static long java.lang.Long.highestOneBit(long)
+..<= public static long java.lang.Long.highestOneBit(long) -> <class java.lang.Long: 0>
+..=> public static long java.lang.Long.lowestOneBit(long)
+..<= public static long java.lang.Long.lowestOneBit(long) -> <class java.lang.Long: 0>
+..=> public static int java.lang.Long.numberOfLeadingZeros(long)
+..<= public static int java.lang.Long.numberOfLeadingZeros(long) -> <class java.lang.Integer: 64>
+..=> public static int java.lang.Long.numberOfTrailingZeros(long)
+..<= public static int java.lang.Long.numberOfTrailingZeros(long) -> <class java.lang.Integer: 64>
+..=> public static long java.lang.Long.rotateRight(long,int)
+..<= public static long java.lang.Long.rotateRight(long,int) -> <class java.lang.Long: 0>
+..=> public static long java.lang.Long.rotateLeft(long,int)
+..<= public static long java.lang.Long.rotateLeft(long,int) -> <class java.lang.Long: 0>
+..=> public static int java.lang.Long.signum(long)
+..<= public static int java.lang.Long.signum(long) -> <class java.lang.Integer: 0>
+..=> public static short java.lang.Short.reverseBytes(short)
+..<= public static short java.lang.Short.reverseBytes(short) -> <class java.lang.Short: 0>
+..=> public static double java.lang.Math.abs(double)
+..<= public static double java.lang.Math.abs(double) -> <class java.lang.Double: 0.0>
+..=> public static float java.lang.Math.abs(float)
+..<= public static float java.lang.Math.abs(float) -> <class java.lang.Float: 0.0>
+..=> public static long java.lang.Math.abs(long)
+..<= public static long java.lang.Math.abs(long) -> <class java.lang.Long: 0>
+..=> public static int java.lang.Math.abs(int)
+..<= public static int java.lang.Math.abs(int) -> <class java.lang.Integer: 0>
+..=> public static double java.lang.Math.min(double,double)
+..<= public static double java.lang.Math.min(double,double) -> <class java.lang.Double: 0.0>
+..=> public static float java.lang.Math.min(float,float)
+..<= public static float java.lang.Math.min(float,float) -> <class java.lang.Float: 0.0>
+..=> public static long java.lang.Math.min(long,long)
+..<= public static long java.lang.Math.min(long,long) -> <class java.lang.Long: 0>
+..=> public static int java.lang.Math.min(int,int)
+..<= public static int java.lang.Math.min(int,int) -> <class java.lang.Integer: 0>
+..=> public static double java.lang.Math.max(double,double)
+..<= public static double java.lang.Math.max(double,double) -> <class java.lang.Double: 0.0>
+..=> public static float java.lang.Math.max(float,float)
+..<= public static float java.lang.Math.max(float,float) -> <class java.lang.Float: 0.0>
+..=> public static long java.lang.Math.max(long,long)
+..<= public static long java.lang.Math.max(long,long) -> <class java.lang.Long: 0>
+..=> public static int java.lang.Math.max(int,int)
+..<= public static int java.lang.Math.max(int,int) -> <class java.lang.Integer: 0>
+..=> public static double java.lang.Math.cos(double)
+..<= public static double java.lang.Math.cos(double) -> <class java.lang.Double: 1.0>
+..=> public static double java.lang.Math.sin(double)
+..<= public static double java.lang.Math.sin(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.acos(double)
+..<= public static double java.lang.Math.acos(double) -> <class java.lang.Double: 1.5707963267948966>
+..=> public static double java.lang.Math.asin(double)
+..<= public static double java.lang.Math.asin(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.atan(double)
+..<= public static double java.lang.Math.atan(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.atan2(double,double)
+..<= public static double java.lang.Math.atan2(double,double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.cbrt(double)
+..<= public static double java.lang.Math.cbrt(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.cosh(double)
+..<= public static double java.lang.Math.cosh(double) -> <class java.lang.Double: 1.0>
+..=> public static double java.lang.Math.exp(double)
+..<= public static double java.lang.Math.exp(double) -> <class java.lang.Double: 1.0>
+..=> public static double java.lang.Math.expm1(double)
+..<= public static double java.lang.Math.expm1(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.hypot(double,double)
+..<= public static double java.lang.Math.hypot(double,double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.log(double)
+..<= public static double java.lang.Math.log(double) -> <class java.lang.Double: -Infinity>
+..=> public static double java.lang.Math.log10(double)
+..<= public static double java.lang.Math.log10(double) -> <class java.lang.Double: -Infinity>
+..=> public static double java.lang.Math.nextAfter(double,double)
+..<= public static double java.lang.Math.nextAfter(double,double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.sinh(double)
+..<= public static double java.lang.Math.sinh(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.tan(double)
+..<= public static double java.lang.Math.tan(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.tanh(double)
+..<= public static double java.lang.Math.tanh(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.sqrt(double)
+..<= public static double java.lang.Math.sqrt(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.ceil(double)
+..<= public static double java.lang.Math.ceil(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.floor(double)
+..<= public static double java.lang.Math.floor(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.rint(double)
+..<= public static double java.lang.Math.rint(double) -> <class java.lang.Double: 0.0>
+..=> public static long java.lang.Math.round(double)
+..<= public static long java.lang.Math.round(double) -> <class java.lang.Long: 0>
+..=> public static int java.lang.Math.round(float)
+..<= public static int java.lang.Math.round(float) -> <class java.lang.Integer: 0>
+..=> public static java.lang.Thread java.lang.Thread.currentThread()
+..<= public static java.lang.Thread java.lang.Thread.currentThread() -> <<non-deterministic>: <non-deterministic>>
+..=> public char java.lang.String.charAt(int)
+..<= public char java.lang.String.charAt(int) -> <class java.lang.Character: s>
+..=> public int java.lang.String.compareTo(java.lang.String)
+..<= public int java.lang.String.compareTo(java.lang.String) -> <class java.lang.Integer: 11>
+..=> public boolean java.lang.String.equals(java.lang.Object)
+..<= public boolean java.lang.String.equals(java.lang.Object) -> <class java.lang.Boolean: false>
+..=> public int java.lang.String.indexOf(int)
+..<= public int java.lang.String.indexOf(int) -> <class java.lang.Integer: -1>
+..=> public int java.lang.String.indexOf(int,int)
+..<= public int java.lang.String.indexOf(int,int) -> <class java.lang.Integer: -1>
+..=> public int java.lang.String.indexOf(java.lang.String)
+..<= public int java.lang.String.indexOf(java.lang.String) -> <class java.lang.Integer: -1>
+..=> public int java.lang.String.indexOf(java.lang.String,int)
+..<= public int java.lang.String.indexOf(java.lang.String,int) -> <class java.lang.Integer: -1>
+..=> public boolean java.lang.String.isEmpty()
+..<= public boolean java.lang.String.isEmpty() -> <class java.lang.Boolean: false>
+..=> public int java.lang.String.length()
+..<= public int java.lang.String.length() -> <class java.lang.Integer: 17>
+..=> public synchronized java.lang.StringBuffer java.lang.StringBuffer.append(java.lang.String)
+..<= public synchronized java.lang.StringBuffer java.lang.StringBuffer.append(java.lang.String) -> <class java.lang.StringBuffer: some large string bufferhello>
+..=> public synchronized int java.lang.StringBuffer.length()
+..<= public synchronized int java.lang.StringBuffer.length() -> <class java.lang.Integer: 29>
+..=> public synchronized java.lang.String java.lang.StringBuffer.toString()
+..<= public synchronized java.lang.String java.lang.StringBuffer.toString() -> <class java.lang.String: some large string bufferhello>
+..=> public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String)
+..<= public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: some large string builderhello>
+..=> public int java.lang.StringBuilder.length()
+..<= public int java.lang.StringBuilder.length() -> <class java.lang.Integer: 30>
+..=> public java.lang.String java.lang.StringBuilder.toString()
+..<= public java.lang.String java.lang.StringBuilder.toString() -> <class java.lang.String: some large string builderhello>
+..=> public static java.lang.Integer java.lang.Integer.valueOf(int)
+..<= public static java.lang.Integer java.lang.Integer.valueOf(int) -> <class java.lang.Integer: 0>
+..=> public static boolean java.lang.Thread.interrupted()
+..<= public static boolean java.lang.Thread.interrupted() -> <class java.lang.Boolean: false>
+.<= static void art.Test988Intrinsics.test() -> <null: null>
+.=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
+.<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null>
+.=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
+.<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null>
+<= static void art.Test988$IntrinsicsTest.doTest() -> <null: null>
+=> public static java.lang.Thread java.lang.Thread.currentThread()
+<= public static java.lang.Thread java.lang.Thread.currentThread() -> <<non-deterministic>: <non-deterministic>>
+=> public static void art.Trace.disableTracing(java.lang.Thread)
diff --git a/test/988-method-trace/expected_jack.diff b/test/988-method-trace/expected_jack.diff
new file mode 100644
index 0000000000..11364a0539
--- /dev/null
+++ b/test/988-method-trace/expected_jack.diff
@@ -0,0 +1,10 @@
+450,453c450,453
+< .=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
+< .<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null>
+< .=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
+< .<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null>
+---
+> .=> public static void java.lang.System.arraycopy(int[],int,int[],int,int)
+> .<= public static void java.lang.System.arraycopy(int[],int,int[],int,int) -> <null: null>
+> .=> public static void java.lang.System.arraycopy(char[],int,char[],int,int)
+> .<= public static void java.lang.System.arraycopy(char[],int,char[],int,int) -> <null: null>
diff --git a/test/988-method-trace/gen_srcs.py b/test/988-method-trace/gen_srcs.py
new file mode 100755
index 0000000000..c1ce35c278
--- /dev/null
+++ b/test/988-method-trace/gen_srcs.py
@@ -0,0 +1,321 @@
+#!/usr/bin/python3
+# 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.
+
+#
+# Generates the src/art/Test988Intrinsics.java file.
+# Re-run this every time art/compiler/intrinics_list.h is modified.
+#
+# $> python3.4 gen_srcs.py > src/art/Test988Intrinsics.java
+#
+
+import argparse
+import os
+import re
+import collections
+import sys
+
+from string import Template
+
+# Relative path to art/compiler/intrinsics_list.h
+INTRINSICS_LIST_H = os.path.dirname(os.path.realpath(__file__)) + "/../../compiler/intrinsics_list.h"
+
+# Macro parameter index to V(). Negative means from the end.
+IDX_STATIC_OR_VIRTUAL = 1
+IDX_SIGNATURE = -1
+IDX_METHOD_NAME = -2
+IDX_CLASS_NAME = -3
+
+# Exclude all hidden API.
+KLASS_BLACK_LIST = ['sun.misc.Unsafe', 'libcore.io.Memory', 'java.lang.StringFactory']
+METHOD_BLACK_LIST = [('java.lang.ref.Reference', 'getReferent'),
+ ('java.lang.String', 'getCharsNoCheck'),
+ ('java.lang.System', 'arraycopy')] # arraycopy has a manual test.
+
+# When testing a virtual function, it needs to operate on an instance.
+# These instances will be created with the following values,
+# otherwise a default 'new T()' is used.
+KLASS_INSTANCE_INITIALIZERS = {
+ 'java.lang.String' : '"some large string"',
+ 'java.lang.StringBuffer' : 'new java.lang.StringBuffer("some large string buffer")',
+ 'java.lang.StringBuilder' : 'new java.lang.StringBuilder("some large string builder")',
+ 'java.lang.ref.Reference' : 'new java.lang.ref.WeakReference(new Object())'
+};
+
+OUTPUT_TPL = Template("""
+/*
+ * 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.
+ */
+
+// AUTO-GENENERATED by gen_srcs.py: DO NOT EDIT HERE DIRECTLY.
+//
+// $$> python3.4 gen_srcs.py > src/art/Test988Intrinsics.java
+//
+// RUN ABOVE COMMAND TO REGENERATE THIS FILE.
+
+package art;
+
+class Test988Intrinsics {
+ // Pre-initialize *all* instance variables used so that their constructors are not in the trace.
+$static_fields
+
+ static void initialize() {
+ // Ensure all static variables are initialized.
+ // In addition, pre-load classes here so that we don't see diverging class loading traces.
+$initialize_classes
+ }
+
+ static void test() {
+ // Call each intrinsic from art/compiler/intrinsics_list.h to make sure they are traced.
+$test_body
+ }
+}
+""")
+
+JNI_TYPES = {
+ 'Z' : 'boolean',
+ 'B' : 'byte',
+ 'C' : 'char',
+ 'S' : 'short',
+ 'I' : 'int',
+ 'J' : 'long',
+ 'F' : 'float',
+ 'D' : 'double',
+ 'L' : 'object'
+};
+
+debug_printing_enabled = False
+
+def debug_print(x):
+ if debug_printing_enabled:
+ print(x, file=sys.stderr)
+
+# Parse JNI sig into a list, e.g. "II" -> ['I', 'I'], '[[IJ' -> ['[[I', 'J'], etc.
+def sig_to_parameter_type_list(sig):
+ sig = re.sub(r'[(](.*)[)].*', r'\1', sig)
+
+ lst = []
+ obj = ""
+ is_obj = False
+ is_array = False
+ for x in sig:
+ if is_obj:
+ obj = obj + x
+ if x == ";":
+ is_obj = False
+ lst.append(obj)
+ obj = ""
+ elif is_array:
+ obj = obj + x
+ if x != "[":
+ is_array = False
+ lst.append(obj)
+ obj = ""
+ else:
+ if x == "[":
+ obj = "["
+ is_array = True
+ elif x == "L":
+ obj = "L"
+ is_obj = True
+ else:
+ lst.append(x)
+
+ return lst
+
+# Convert a single JNI descriptor into a pretty java name, e.g. "[I" -> "int[]", etc.
+def javafy_name(kls_name):
+ if kls_name.startswith("L"):
+ kls_name = kls_name.lstrip("L").rstrip(";")
+ return kls_name.replace("/", ".")
+ elif kls_name.startswith("["):
+ array_count = kls_name.count("[")
+ non_array = javafy_name(kls_name.lstrip("["))
+ return non_array + ("[]" * array_count)
+
+ return JNI_TYPES.get(kls_name, kls_name)
+
+def extract_staticness(static_or_virtual):
+ if static_or_virtual == "kStatic":
+ return 'static'
+ return 'virtual' # kVirtual, kDirect
+
+class MethodInfo:
+ def __init__(self, staticness, pretty_params, method, kls):
+ # 'virtual' or 'static'
+ self.staticness = staticness
+ # list of e.g. ['int', 'double', 'java.lang.String'] etc
+ self.parameters = pretty_params
+ # e.g. 'toString'
+ self.method_name = method
+ # e.g. 'java.lang.String'
+ self.klass = kls
+
+ def __str__(self):
+ return "MethodInfo " + str(self.__dict__)
+
+ def dummy_parameters(self):
+ dummy_values = {
+ 'boolean' : 'false',
+ 'byte' : '(byte)0',
+ 'char' : "'x'",
+ 'short' : '(short)0',
+ 'int' : '0',
+ 'long' : '0L',
+ 'float' : '0.0f',
+ 'double' : '0.0'
+ }
+
+ def object_dummy(name):
+ if name == "java.lang.String":
+ return '"hello"'
+ else:
+ return "(%s)null" %(name)
+ return [ dummy_values.get(param, object_dummy(param)) for param in self.parameters ]
+
+ def dummy_instance_value(self):
+ return KLASS_INSTANCE_INITIALIZERS.get(self.klass, 'new %s()' %(self.klass))
+
+ def is_blacklisted(self):
+ for blk in KLASS_BLACK_LIST:
+ if self.klass.startswith(blk):
+ return True
+
+ return (self.klass, self.method_name) in METHOD_BLACK_LIST
+
+# parse the V(...) \ list of items into a MethodInfo
+def parse_method_info(items):
+ def get_item(idx):
+ return items[idx].strip().strip("\"")
+
+ staticness = get_item(IDX_STATIC_OR_VIRTUAL)
+ sig = get_item(IDX_SIGNATURE)
+ method = get_item(IDX_METHOD_NAME)
+ kls = get_item(IDX_CLASS_NAME)
+
+ debug_print ((sig, method, kls))
+
+ staticness = extract_staticness(staticness)
+ kls = javafy_name(kls)
+ param_types = sig_to_parameter_type_list(sig)
+ pretty_params = param_types
+ pretty_params = [javafy_name(i) for i in param_types]
+
+ return MethodInfo(staticness, pretty_params, method, kls)
+
+# parse a line containing ' V(...)' into a MethodInfo
+def parse_line(line):
+ line = line.strip()
+ if not line.startswith("V("):
+ return None
+
+ line = re.sub(r'V[(](.*)[)]', r'\1', line)
+ debug_print(line)
+
+ items = line.split(",")
+
+ method_info = parse_method_info(items)
+ return method_info
+
+# Generate all the MethodInfo that we parse from intrinsics_list.h
+def parse_all_method_infos():
+ with open(INTRINSICS_LIST_H) as f:
+ for line in f:
+ s = parse_line(line)
+ if s is not None:
+ yield s
+
+# Format a receiver name. For statics, it's the class name, for receivers, it's an instance variable
+def format_receiver_name(method_info):
+ receiver = method_info.klass
+ if method_info.staticness == 'virtual':
+ receiver = "instance_" + method_info.klass.replace(".", "_")
+ return receiver
+
+# Format a dummy call with dummy method parameters to the requested method.
+def format_call_to(method_info):
+ dummy_args = ", ".join(method_info.dummy_parameters())
+ receiver = format_receiver_name(method_info)
+
+ return ("%s.%s(%s);" %(receiver, method_info.method_name, dummy_args))
+
+# Format a static variable with an instance that could be used as the receiver
+# (or None for non-static methods).
+def format_instance_variable(method_info):
+ if method_info.staticness == 'static':
+ return None
+ return "static %s %s = %s;" %(method_info.klass, format_receiver_name(method_info), method_info.dummy_instance_value())
+
+def format_initialize_klass(method_info):
+ return "%s.class.toString();" %(method_info.klass)
+
+def indent_list(lst, indent):
+ return [' ' * indent + i for i in lst]
+
+def main():
+ global debug_printing_enabled
+ parser = argparse.ArgumentParser(description='Generate art/test/988-method-trace/src/art/Test988Intrinsics.java')
+ parser.add_argument('-d', '--debug', action='store_true', dest='debug', help='Print extra debugging information to stderr.')
+ parser.add_argument('output_file', nargs='?', metavar='<output-file>', default=sys.stdout, type=argparse.FileType('w'), help='Destination file to write to (default: stdout).')
+ args = parser.parse_args()
+
+ debug_printing_enabled = args.debug
+
+ #####
+
+ call_str_list = []
+ instance_variable_dict = collections.OrderedDict()
+ initialize_klass_dict = collections.OrderedDict()
+ for i in parse_all_method_infos():
+ debug_print(i)
+ if i.is_blacklisted():
+ debug_print("Blacklisted: " + str(i))
+ continue
+
+ call_str = format_call_to(i)
+ debug_print(call_str)
+
+ call_str_list.append(call_str)
+
+ instance_variable = format_instance_variable(i)
+ if instance_variable is not None:
+ debug_print(instance_variable)
+ instance_variable_dict[i.klass] = instance_variable
+
+ initialize_klass_dict[i.klass] = format_initialize_klass(i)
+
+ static_fields = indent_list([ value for (key, value) in instance_variable_dict.items() ], 2)
+ test_body = indent_list(call_str_list, 4)
+ initialize_classes = indent_list([ value for (key, value) in initialize_klass_dict.items() ], 4)
+
+ print(OUTPUT_TPL.substitute(static_fields="\n".join(static_fields),
+ test_body="\n".join(test_body),
+ initialize_classes="\n".join(initialize_classes)).
+ strip("\n"), \
+ file=args.output_file)
+
+if __name__ == '__main__':
+ main()
diff --git a/test/988-method-trace/src/art/Test988.java b/test/988-method-trace/src/art/Test988.java
index e40c612851..d7eda524c4 100644
--- a/test/988-method-trace/src/art/Test988.java
+++ b/test/988-method-trace/src/art/Test988.java
@@ -57,7 +57,7 @@ public class Test988 {
}
@Override
public void Print() {
- System.out.println(whitespace(cnt) + "=> " + m);
+ System.out.println(whitespace(cnt) + "=> " + methodToString(m));
}
}
@@ -124,6 +124,13 @@ public class Test988 {
}
}
+ static String methodToString(Object m) {
+ // Make the output more similar between ART and RI,
+ // by removing the 'native' specifier from methods.
+ String methodStr = m.toString();
+ return methodStr.replaceFirst(" native", "");
+ }
+
static final class MethodReturn implements Printable {
private Object m;
private Object val;
@@ -154,7 +161,7 @@ public class Test988 {
klass_print = klass.toString();
}
System.out.println(
- whitespace(cnt) + "<= " + m + " -> <" + klass_print + ": " + print + ">");
+ whitespace(cnt) + "<= " + methodToString(m) + " -> <" + klass_print + ": " + print + ">");
}
}
@@ -167,7 +174,7 @@ public class Test988 {
}
@Override
public void Print() {
- System.out.println(whitespace(cnt) + "<= " + m + " EXCEPTION");
+ System.out.println(whitespace(cnt) + "<= " + methodToString(m) + " EXCEPTION");
}
}
@@ -255,15 +262,26 @@ public class Test988 {
}
}
+ static final int METHOD_TRACING_IGNORE_DEPTH = 2;
+ static boolean sMethodTracingIgnore = false;
+
public static void notifyMethodEntry(Object m) {
// Called by native code when a method is entered. This method is ignored by the native
// entry and exit hooks.
- results.add(new MethodEntry(m, cnt));
cnt++;
+ if ((cnt - 1) > METHOD_TRACING_IGNORE_DEPTH && sMethodTracingIgnore) {
+ return;
+ }
+ results.add(new MethodEntry(m, cnt - 1));
}
public static void notifyMethodExit(Object m, boolean exception, Object result) {
cnt--;
+
+ if (cnt > METHOD_TRACING_IGNORE_DEPTH && sMethodTracingIgnore) {
+ return;
+ }
+
if (exception) {
results.add(new MethodThrownThrough(m, cnt));
} else {
@@ -285,6 +303,10 @@ public class Test988 {
doFibTest(5, new RecurOp());
doFibTest(-19, new IterOp());
doFibTest(-19, new RecurOp());
+
+ sMethodTracingIgnore = true;
+ IntrinsicsTest.doTest();
+ sMethodTracingIgnore = false;
// Turn off method tracing so we don't have to deal with print internals.
Trace.disableTracing(Thread.currentThread());
printResults();
@@ -303,6 +325,7 @@ public class Test988 {
RecurOp.class.toString();
IterOp.class.toString();
StringBuilder.class.toString();
+ IntrinsicsTest.initialize(); // ensure <clinit> is executed prior to tracing.
}
public static void printResults() {
@@ -319,4 +342,30 @@ public class Test988 {
results.add(new FibThrow("fibonacci(%d) -> %s\n", x, t));
}
}
+
+ static class IntrinsicsTest {
+ static int[] sSourceArray = { 0, 1, 2, 3, 4, 5 };
+ static int[] sDestArray = { 5, 6, 7, 8, 9, 10 };
+
+ static char[] sSourceArrayChar = { '0', '1', '2', '3', '4', '5' };
+ static char[] sDestArrayChar = { '5', '6', '7', '8', '9', 'a' };
+
+ static void initialize() {
+ Test988Intrinsics.initialize();
+
+ // Pre-load all classes used in #doTest manual intrinsics.
+ java.lang.System.class.toString();
+ }
+ static void doTest() {
+ // Ensure that the ART intrinsics in intrinsics_list.h are also being traced,
+ // since in non-tracing operation they are effectively inlined by the optimizing compiler.
+
+ // Auto-generated test file that uses null/0s as default parameters.
+ Test988Intrinsics.test();
+
+ // Manual list here for functions that require special non-null/non-zero parameters:
+ System.arraycopy(sSourceArray, 0, sDestArray, 0, 1);
+ System.arraycopy(sSourceArrayChar, 0, sDestArrayChar, 0, 1);
+ }
+ }
}
diff --git a/test/988-method-trace/src/art/Test988Intrinsics.java b/test/988-method-trace/src/art/Test988Intrinsics.java
new file mode 100644
index 0000000000..099fbf2ce8
--- /dev/null
+++ b/test/988-method-trace/src/art/Test988Intrinsics.java
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+
+// AUTO-GENENERATED by gen_srcs.py: DO NOT EDIT HERE DIRECTLY.
+//
+// $> python3.4 gen_srcs.py > src/art/Test988Intrinsics.java
+//
+// RUN ABOVE COMMAND TO REGENERATE THIS FILE.
+
+package art;
+
+class Test988Intrinsics {
+ // Pre-initialize *all* instance variables used so that their constructors are not in the trace.
+ static java.lang.String instance_java_lang_String = "some large string";
+ static java.lang.StringBuffer instance_java_lang_StringBuffer = new java.lang.StringBuffer("some large string buffer");
+ static java.lang.StringBuilder instance_java_lang_StringBuilder = new java.lang.StringBuilder("some large string builder");
+
+ static void initialize() {
+ // Ensure all static variables are initialized.
+ // In addition, pre-load classes here so that we don't see diverging class loading traces.
+ java.lang.Double.class.toString();
+ java.lang.Float.class.toString();
+ java.lang.Integer.class.toString();
+ java.lang.Long.class.toString();
+ java.lang.Short.class.toString();
+ java.lang.Math.class.toString();
+ java.lang.Thread.class.toString();
+ java.lang.String.class.toString();
+ java.lang.StringBuffer.class.toString();
+ java.lang.StringBuilder.class.toString();
+ }
+
+ static void test() {
+ // Call each intrinsic from art/compiler/intrinsics_list.h to make sure they are traced.
+ java.lang.Double.doubleToRawLongBits(0.0);
+ java.lang.Double.doubleToLongBits(0.0);
+ java.lang.Double.isInfinite(0.0);
+ java.lang.Double.isNaN(0.0);
+ java.lang.Double.longBitsToDouble(0L);
+ java.lang.Float.floatToRawIntBits(0.0f);
+ java.lang.Float.floatToIntBits(0.0f);
+ java.lang.Float.isInfinite(0.0f);
+ java.lang.Float.isNaN(0.0f);
+ java.lang.Float.intBitsToFloat(0);
+ java.lang.Integer.reverse(0);
+ java.lang.Integer.reverseBytes(0);
+ java.lang.Integer.bitCount(0);
+ java.lang.Integer.compare(0, 0);
+ java.lang.Integer.highestOneBit(0);
+ java.lang.Integer.lowestOneBit(0);
+ java.lang.Integer.numberOfLeadingZeros(0);
+ java.lang.Integer.numberOfTrailingZeros(0);
+ java.lang.Integer.rotateRight(0, 0);
+ java.lang.Integer.rotateLeft(0, 0);
+ java.lang.Integer.signum(0);
+ java.lang.Long.reverse(0L);
+ java.lang.Long.reverseBytes(0L);
+ java.lang.Long.bitCount(0L);
+ java.lang.Long.compare(0L, 0L);
+ java.lang.Long.highestOneBit(0L);
+ java.lang.Long.lowestOneBit(0L);
+ java.lang.Long.numberOfLeadingZeros(0L);
+ java.lang.Long.numberOfTrailingZeros(0L);
+ java.lang.Long.rotateRight(0L, 0);
+ java.lang.Long.rotateLeft(0L, 0);
+ java.lang.Long.signum(0L);
+ java.lang.Short.reverseBytes((short)0);
+ java.lang.Math.abs(0.0);
+ java.lang.Math.abs(0.0f);
+ java.lang.Math.abs(0L);
+ java.lang.Math.abs(0);
+ java.lang.Math.min(0.0, 0.0);
+ java.lang.Math.min(0.0f, 0.0f);
+ java.lang.Math.min(0L, 0L);
+ java.lang.Math.min(0, 0);
+ java.lang.Math.max(0.0, 0.0);
+ java.lang.Math.max(0.0f, 0.0f);
+ java.lang.Math.max(0L, 0L);
+ java.lang.Math.max(0, 0);
+ java.lang.Math.cos(0.0);
+ java.lang.Math.sin(0.0);
+ java.lang.Math.acos(0.0);
+ java.lang.Math.asin(0.0);
+ java.lang.Math.atan(0.0);
+ java.lang.Math.atan2(0.0, 0.0);
+ java.lang.Math.cbrt(0.0);
+ java.lang.Math.cosh(0.0);
+ java.lang.Math.exp(0.0);
+ java.lang.Math.expm1(0.0);
+ java.lang.Math.hypot(0.0, 0.0);
+ java.lang.Math.log(0.0);
+ java.lang.Math.log10(0.0);
+ java.lang.Math.nextAfter(0.0, 0.0);
+ java.lang.Math.sinh(0.0);
+ java.lang.Math.tan(0.0);
+ java.lang.Math.tanh(0.0);
+ java.lang.Math.sqrt(0.0);
+ java.lang.Math.ceil(0.0);
+ java.lang.Math.floor(0.0);
+ java.lang.Math.rint(0.0);
+ java.lang.Math.round(0.0);
+ java.lang.Math.round(0.0f);
+ java.lang.Thread.currentThread();
+ instance_java_lang_String.charAt(0);
+ instance_java_lang_String.compareTo("hello");
+ instance_java_lang_String.equals((java.lang.Object)null);
+ instance_java_lang_String.indexOf(0);
+ instance_java_lang_String.indexOf(0, 0);
+ instance_java_lang_String.indexOf("hello");
+ instance_java_lang_String.indexOf("hello", 0);
+ instance_java_lang_String.isEmpty();
+ instance_java_lang_String.length();
+ instance_java_lang_StringBuffer.append("hello");
+ instance_java_lang_StringBuffer.length();
+ instance_java_lang_StringBuffer.toString();
+ instance_java_lang_StringBuilder.append("hello");
+ instance_java_lang_StringBuilder.length();
+ instance_java_lang_StringBuilder.toString();
+ java.lang.Integer.valueOf(0);
+ java.lang.Thread.interrupted();
+ }
+}
diff --git a/test/Android.bp b/test/Android.bp
index f893531c41..591684b887 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -286,6 +286,8 @@ art_cc_defaults {
"992-source-data/source_file.cc",
"993-breakpoints/breakpoints.cc",
"996-breakpoint-obsolete/obsolete_breakpoints.cc",
+ "1900-track-alloc/alloc.cc",
+ "1901-get-bytecodes/bytecodes.cc",
],
shared_libs: [
"libbase",
@@ -445,6 +447,7 @@ art_cc_test_library {
"art_debug_defaults",
"art_defaults",
],
+ header_libs: ["libnativebridge-dummy-headers"],
srcs: ["115-native-bridge/nativebridge.cc"],
target: {
android: {
diff --git a/test/Android.run-test-jvmti-java-library.mk b/test/Android.run-test-jvmti-java-library.mk
deleted file mode 100644
index 753fe9a330..0000000000
--- a/test/Android.run-test-jvmti-java-library.mk
+++ /dev/null
@@ -1,171 +0,0 @@
-#
-# 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# shim classes. We use one that exposes the common functionality.
-LOCAL_SHIM_CLASSES := \
- 902-hello-transformation/src/art/Redefinition.java \
- 903-hello-tagging/src/art/Main.java \
- 989-method-trace-throw/src/art/Trace.java \
-
-LOCAL_SRC_FILES := $(LOCAL_SHIM_CLASSES)
-
-# Actual test classes.
-LOCAL_SRC_FILES += \
- 901-hello-ti-agent/src/art/Test901.java \
- 902-hello-transformation/src/art/Test902.java \
- 903-hello-tagging/src/art/Test903.java \
- 904-object-allocation/src/art/Test904.java \
- 905-object-free/src/art/Test905.java \
- 906-iterate-heap/src/art/Test906.java \
- 907-get-loaded-classes/src/art/Test907.java \
- 907-get-loaded-classes/src/art/Cerr.java \
- 908-gc-start-finish/src/art/Test908.java \
- 910-methods/src/art/Test910.java \
- 911-get-stack-trace/src/art/Test911.java \
- 911-get-stack-trace/src/art/AllTraces.java \
- 911-get-stack-trace/src/art/ControlData.java \
- 911-get-stack-trace/src/art/Frames.java \
- 911-get-stack-trace/src/art/OtherThread.java \
- 911-get-stack-trace/src/art/PrintThread.java \
- 911-get-stack-trace/src/art/Recurse.java \
- 911-get-stack-trace/src/art/SameThread.java \
- 911-get-stack-trace/src/art/ThreadListTraces.java \
- 912-classes/src-art/art/Test912.java \
- 912-classes/src-art/art/DexData.java \
- 913-heaps/src/art/Test913.java \
- 914-hello-obsolescence/src/art/Test914.java \
- 915-obsolete-2/src/art/Test915.java \
- 917-fields-transformation/src/art/Test917.java \
- 918-fields/src/art/Test918.java \
- 919-obsolete-fields/src/art/Test919.java \
- 920-objects/src/art/Test920.java \
- 922-properties/src/art/Test922.java \
- 923-monitors/src/art/Test923.java \
- 924-threads/src/art/Test924.java \
- 925-threadgroups/src/art/Test925.java \
- 926-multi-obsolescence/src/art/Test926.java \
- 927-timers/src/art/Test927.java \
- 928-jni-table/src/art/Test928.java \
- 930-hello-retransform/src/art/Test930.java \
- 931-agent-thread/src/art/Test931.java \
- 932-transform-saves/src/art/Test932.java \
- 933-misc-events/src/art/Test933.java \
- 940-recursive-obsolete/src/art/Test940.java \
- 942-private-recursive/src/art/Test942.java \
- 944-transform-classloaders/src/art/Test944.java \
- 945-obsolete-native/src/art/Test945.java \
- 947-reflect-method/src/art/Test947.java \
- 951-threaded-obsolete/src/art/Test951.java \
- 981-dedup-original-dex/src-art/art/Test981.java \
- 982-ok-no-retransform/src/art/Test982.java \
- 984-obsolete-invoke/src/art/Test984.java \
- 985-re-obsolete/src/art/Test985.java \
- 986-native-method-bind/src/art/Test986.java \
- 988-method-trace/src/art/Test988.java \
- 989-method-trace-throw/src/art/Test989.java \
- 990-field-trace/src/art/Test990.java \
- 991-field-trace-2/src/art/Test991.java \
- 992-source-data/src/art/Test992.java \
- 992-source-data/src/art/Target2.java \
-
-JVMTI_RUN_TEST_GENERATED_NUMBERS := \
- 901 \
- 902 \
- 903 \
- 904 \
- 905 \
- 906 \
- 907 \
- 908 \
- 910 \
- 911 \
- 912 \
- 913 \
- 914 \
- 915 \
- 917 \
- 918 \
- 919 \
- 920 \
- 922 \
- 923 \
- 924 \
- 925 \
- 926 \
- 927 \
- 928 \
- 930 \
- 931 \
- 932 \
- 933 \
- 940 \
- 942 \
- 944 \
- 945 \
- 947 \
- 951 \
- 981 \
- 982 \
- 984 \
- 985 \
- 986 \
- 988 \
- 989 \
- 990 \
- 991 \
- 992 \
-
-# Try to enforce that the directories correspond to the Java files we pull in.
-JVMTI_RUN_TEST_DIR_CHECK := $(sort $(foreach DIR,$(JVMTI_RUN_TEST_GENERATED_NUMBERS), \
- $(filter $(DIR)%,$(LOCAL_SRC_FILES))))
-ifneq ($(sort $(LOCAL_SRC_FILES)),$(JVMTI_RUN_TEST_DIR_CHECK))
- $(error Missing file, compare $(sort $(LOCAL_SRC_FILES)) with $(JVMTI_RUN_TEST_DIR_CHECK))
-endif
-
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_MODULE_TAGS := optional
-LOCAL_JAVA_LANGUAGE_VERSION := 1.8
-LOCAL_MODULE := run-test-jvmti-java
-
-GENERATED_SRC_DIR := $(call local-generated-sources-dir)
-JVMTI_RUN_TEST_GENERATED_FILES := \
- $(foreach NR,$(JVMTI_RUN_TEST_GENERATED_NUMBERS),$(GENERATED_SRC_DIR)/results.$(NR).expected.txt)
-
-define GEN_JVMTI_RUN_TEST_GENERATED_FILE
-
-GEN_INPUT := $(wildcard $(LOCAL_PATH)/$(1)*/expected.txt)
-GEN_OUTPUT := $(GENERATED_SRC_DIR)/results.$(1).expected.txt
-$$(GEN_OUTPUT): $$(GEN_INPUT)
- cp $$< $$@
-
-GEN_INPUT :=
-GEN_OUTPUT :=
-
-endef
-
-$(foreach NR,$(JVMTI_RUN_TEST_GENERATED_NUMBERS),\
- $(eval $(call GEN_JVMTI_RUN_TEST_GENERATED_FILE,$(NR))))
-LOCAL_JAVA_RESOURCE_FILES := $(JVMTI_RUN_TEST_GENERATED_FILES)
-
-# We only want to depend on libcore.
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-all
-
-include $(BUILD_JAVA_LIBRARY)
diff --git a/test/ForClassLoaderA/Classes.java b/test/ForClassLoaderA/Classes.java
new file mode 100644
index 0000000000..a65ef64161
--- /dev/null
+++ b/test/ForClassLoaderA/Classes.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class DefinedInA {
+}
+
+class DefinedInAB {
+}
+
+class DefinedInABC {
+}
+
+class DefinedInAC {
+}
+
+class DefinedInAD {
+}
+
diff --git a/test/ForClassLoaderB/Classes.java b/test/ForClassLoaderB/Classes.java
new file mode 100644
index 0000000000..8c85ed5fc0
--- /dev/null
+++ b/test/ForClassLoaderB/Classes.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class DefinedInB {
+}
+
+class DefinedInAB {
+}
+
+class DefinedInABC {
+}
+
+class DefinedInBC {
+}
+
+class DefinedInBD {
+}
diff --git a/test/ForClassLoaderC/Classes.java b/test/ForClassLoaderC/Classes.java
new file mode 100644
index 0000000000..7b9e83ffff
--- /dev/null
+++ b/test/ForClassLoaderC/Classes.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class DefinedInC {
+}
+
+class DefinedInAC {
+}
+
+class DefinedInABC {
+}
+
+class DefinedInBC {
+}
+
+class DefinedInCD {
+}
diff --git a/test/ForClassLoaderD/Classes.java b/test/ForClassLoaderD/Classes.java
new file mode 100644
index 0000000000..b34177f05f
--- /dev/null
+++ b/test/ForClassLoaderD/Classes.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.
+ */
+
+class DefinedInD {
+}
+
+class DefinedInAD {
+}
+
+class DefinedInBD {
+}
+
+class DefinedInCD {
+}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index d3d92c651e..3939bd162f 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -538,7 +538,8 @@
"595-profile-saving",
"900-hello-plugin",
"909-attach-agent",
- "981-dedup-original-dex"
+ "981-dedup-original-dex",
+ "1900-track-alloc"
],
"description": ["Tests that require exact knowledge of the number of plugins and agents."],
"variant": "jvmti-stress | redefine-stress | trace-stress | field-stress | step-stress"
@@ -594,11 +595,12 @@
},
{
"tests": [
- "567-checker-compare"
+ "567-checker-compare",
+ "988-method-trace"
],
- "description": "Checker tests failing when run with --build-with-javac-dx.",
+ "description": "Checker tests fail because desugar lowers Long.compare to lcmp",
"env_vars": {"ANDROID_COMPILE_WITH_JACK": "false"},
- "bug": "b/62950048"
+ "bug": "b/63078894"
},
{
"tests": [
@@ -645,6 +647,11 @@
"env_vars": {"SANITIZE_HOST": "address"}
},
{
+ "tests": "141-class-unload",
+ "description": "Idk why this fails",
+ "env_vars": {"SANITIZE_HOST": "address"}
+ },
+ {
"tests": ["988-method-trace"],
"variant": "redefine-stress | jvmti-stress",
"description": "Test disabled due to redefine-stress disabling intrinsics which changes the trace output slightly."