Merge "Use TestCodeGeneratorX86 in codegen unit tests"
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index 7395164..0cd90c9 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -264,20 +264,46 @@
art_host_non_debug_cflags := $(art_non_debug_cflags)
art_target_non_debug_cflags := $(art_non_debug_cflags)
+###
+# Frame size
+###
+
+# Size of the stack-overflow gap.
+ART_STACK_OVERFLOW_GAP_arm := 8192
+ART_STACK_OVERFLOW_GAP_arm64 := 8192
+ART_STACK_OVERFLOW_GAP_mips := 16384
+ART_STACK_OVERFLOW_GAP_mips64 := 16384
+ART_STACK_OVERFLOW_GAP_x86 := 8192
+ART_STACK_OVERFLOW_GAP_x86_64 := 8192
+ART_COMMON_STACK_OVERFLOW_DEFINES := \
+ -DART_STACK_OVERFLOW_GAP_arm=$(ART_STACK_OVERFLOW_GAP_arm) \
+ -DART_STACK_OVERFLOW_GAP_arm64=$(ART_STACK_OVERFLOW_GAP_arm64) \
+ -DART_STACK_OVERFLOW_GAP_mips=$(ART_STACK_OVERFLOW_GAP_mips) \
+ -DART_STACK_OVERFLOW_GAP_mips64=$(ART_STACK_OVERFLOW_GAP_mips64) \
+ -DART_STACK_OVERFLOW_GAP_x86=$(ART_STACK_OVERFLOW_GAP_x86) \
+ -DART_STACK_OVERFLOW_GAP_x86_64=$(ART_STACK_OVERFLOW_GAP_x86_64) \
+
+# Keep these as small as possible. We have separate values as we have some host vs target
+# specific code (and previously GCC vs Clang).
+ART_HOST_FRAME_SIZE_LIMIT := 1736
+ART_TARGET_FRAME_SIZE_LIMIT := 1736
+
+# Frame size adaptations for instrumented builds.
+ifdef SANITIZE_TARGET
+ ART_TARGET_FRAME_SIZE_LIMIT := 6400
+endif
+
+# Add frame-size checks for non-debug builds.
ifeq ($(HOST_OS),linux)
- # Larger frame-size for host clang builds today
ifneq ($(ART_COVERAGE),true)
ifneq ($(NATIVE_COVERAGE),true)
- art_host_non_debug_cflags += -Wframe-larger-than=2700
- ifdef SANITIZE_TARGET
- art_target_non_debug_cflags += -Wframe-larger-than=6400
- else
- art_target_non_debug_cflags += -Wframe-larger-than=1736
- endif
+ art_host_non_debug_cflags += -Wframe-larger-than=$(ART_HOST_FRAME_SIZE_LIMIT)
+ art_target_non_debug_cflags += -Wframe-larger-than=$(ART_TARGET_FRAME_SIZE_LIMIT)
endif
endif
endif
+
ART_HOST_CFLAGS := $(art_cflags)
ART_TARGET_CFLAGS := $(art_cflags)
@@ -294,6 +320,10 @@
ART_HOST_CFLAGS += -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS)
ART_HOST_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=default $(art_host_cflags)
+ART_HOST_CFLAGS += -DART_FRAME_SIZE_LIMIT=$(ART_HOST_FRAME_SIZE_LIMIT) \
+ $(ART_COMMON_STACK_OVERFLOW_DEFINES)
+
+
ifndef LIBART_IMG_TARGET_BASE_ADDRESS
$(error LIBART_IMG_TARGET_BASE_ADDRESS unset)
endif
@@ -301,6 +331,9 @@
ART_TARGET_CFLAGS += -DART_TARGET \
-DART_BASE_ADDRESS=$(LIBART_IMG_TARGET_BASE_ADDRESS) \
+ART_TARGET_CFLAGS += -DART_FRAME_SIZE_LIMIT=$(ART_TARGET_FRAME_SIZE_LIMIT) \
+ $(ART_COMMON_STACK_OVERFLOW_DEFINES)
+
ifeq ($(ART_TARGET_LINUX),true)
# Setting ART_TARGET_LINUX to true compiles art/ assuming that the target device
# will be running linux rather than android.
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 1146f95..b57383b 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -24,14 +24,16 @@
// Includes for the types that are being specialized
#include <string>
-#include "unit.h"
-#include "jdwp/jdwp.h"
#include "base/logging.h"
#include "base/time_utils.h"
#include "experimental_flags.h"
#include "gc/collector_type.h"
#include "gc/space/large_object_space.h"
+#include "jdwp/jdwp.h"
#include "jit/profile_saver_options.h"
+#include "plugin.h"
+#include "ti/agent.h"
+#include "unit.h"
namespace art {
@@ -381,6 +383,38 @@
};
template <>
+struct CmdlineType<std::vector<Plugin>> : CmdlineTypeParser<std::vector<Plugin>> {
+ Result Parse(const std::string& args) {
+ assert(false && "Use AppendValues() for a Plugin vector type");
+ return Result::Failure("Unconditional failure: Plugin vector must be appended: " + args);
+ }
+
+ Result ParseAndAppend(const std::string& args,
+ std::vector<Plugin>& existing_value) {
+ existing_value.push_back(Plugin::Create(args));
+ return Result::SuccessNoValue();
+ }
+
+ static const char* Name() { return "std::vector<Plugin>"; }
+};
+
+template <>
+struct CmdlineType<std::vector<ti::Agent>> : CmdlineTypeParser<std::vector<ti::Agent>> {
+ Result Parse(const std::string& args) {
+ assert(false && "Use AppendValues() for an Agent vector type");
+ return Result::Failure("Unconditional failure: Agent vector must be appended: " + args);
+ }
+
+ Result ParseAndAppend(const std::string& args,
+ std::vector<ti::Agent>& existing_value) {
+ existing_value.push_back(ti::Agent::Create(args));
+ return Result::SuccessNoValue();
+ }
+
+ static const char* Name() { return "std::vector<ti::Agent>"; }
+};
+
+template <>
struct CmdlineType<std::vector<std::string>> : CmdlineTypeParser<std::vector<std::string>> {
Result Parse(const std::string& args) {
assert(false && "Use AppendValues() for a string vector type");
@@ -625,6 +659,8 @@
log_verbosity.image = true;
} else if (verbose_options[j] == "systrace-locks") {
log_verbosity.systrace_lock_logging = true;
+ } else if (verbose_options[j] == "agents") {
+ log_verbosity.agents = true;
} else {
return Result::Usage(std::string("Unknown -verbose option ") + verbose_options[j]);
}
@@ -735,6 +771,10 @@
Result ParseAndAppend(const std::string& option, ExperimentalFlags& existing) {
if (option == "none") {
existing = ExperimentalFlags::kNone;
+ } else if (option == "agents") {
+ existing = existing | ExperimentalFlags::kAgents;
+ } else if (option == "runtime-plugins") {
+ existing = existing | ExperimentalFlags::kRuntimePlugins;
} else {
return Result::Failure(std::string("Unknown option '") + option + "'");
}
diff --git a/cmdline/detail/cmdline_parse_argument_detail.h b/cmdline/detail/cmdline_parse_argument_detail.h
index 4b56804..84beff5 100644
--- a/cmdline/detail/cmdline_parse_argument_detail.h
+++ b/cmdline/detail/cmdline_parse_argument_detail.h
@@ -497,7 +497,7 @@
std::function<void(TArg&)> save_argument_;
std::function<TArg&(void)> load_argument_;
};
- } // namespace detail // NOLINT [readability/namespace] [5] [whitespace/comments] [2]
+ } // namespace detail // NOLINT [readability/namespace] [5]
} // namespace art
#endif // ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_
diff --git a/cmdline/detail/cmdline_parser_detail.h b/cmdline/detail/cmdline_parser_detail.h
index 9b43bb0..24dbca2 100644
--- a/cmdline/detail/cmdline_parser_detail.h
+++ b/cmdline/detail/cmdline_parser_detail.h
@@ -35,7 +35,7 @@
private:
template <typename TStream, typename T>
static std::true_type InsertionOperatorTest(TStream& os, const T& value,
- std::remove_reference<decltype(os << value)>* = 0); // NOLINT [whitespace/operators] [3]
+ std::remove_reference<decltype(os << value)>* = 0); // NOLINT [whitespace/operators] [3]
template <typename TStream, typename ... T>
static std::false_type InsertionOperatorTest(TStream& os, const T& ... args);
@@ -53,7 +53,7 @@
private:
template <typename TL, typename TR>
static std::true_type EqualityOperatorTest(const TL& left, const TR& right,
- std::remove_reference<decltype(left == right)>* = 0); // NOLINT [whitespace/operators] [3]
+ std::remove_reference<decltype(left == right)>* = 0); // NOLINT [whitespace/operators] [3]
template <typename TL, typename ... T>
static std::false_type EqualityOperatorTest(const TL& left, const T& ... args);
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 8286033..d0a8335 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -77,10 +77,6 @@
static constexpr bool kTimeCompileMethod = !kIsDebugBuild;
-// Whether classes-to-compile and methods-to-compile are only applied to the boot image, or, when
-// given, too all compilations.
-static constexpr bool kRestrictCompilationFiltersToImage = true;
-
// Print additional info during profile guided compilation.
static constexpr bool kDebugProfileGuidedCompilation = false;
@@ -946,10 +942,6 @@
}
bool CompilerDriver::IsClassToCompile(const char* descriptor) const {
- if (kRestrictCompilationFiltersToImage && !IsBootImage()) {
- return true;
- }
-
if (classes_to_compile_ == nullptr) {
return true;
}
@@ -957,10 +949,6 @@
}
bool CompilerDriver::IsMethodToCompile(const MethodReference& method_ref) const {
- if (kRestrictCompilationFiltersToImage && !IsBootImage()) {
- return true;
- }
-
if (methods_to_compile_ == nullptr) {
return true;
}
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index ab85c12..2bf675d 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -59,8 +59,8 @@
static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<ArmAssembler*>(codegen->GetAssembler())-> // NOLINT
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<ArmAssembler*>(codegen->GetAssembler())-> // NOLINT
#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, x).Int32Value()
class NullCheckSlowPathARM : public SlowPathCode {
@@ -706,8 +706,8 @@
};
#undef __
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<ArmAssembler*>(GetAssembler())-> // NOLINT
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<ArmAssembler*>(GetAssembler())-> // NOLINT
inline Condition ARMCondition(IfCondition cond) {
switch (cond) {
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 9ceb310..3b8a521 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -131,8 +131,8 @@
return ARM64ReturnLocation(return_type);
}
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<CodeGeneratorARM64*>(codegen)->GetVIXLAssembler()-> // NOLINT
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<CodeGeneratorARM64*>(codegen)->GetVIXLAssembler()-> // NOLINT
#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArm64PointerSize, x).Int32Value()
// Calculate memory accessing operand for save/restore live registers.
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 59e103a..58879bc 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -145,8 +145,8 @@
return MipsReturnLocation(type);
}
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<CodeGeneratorMIPS*>(codegen)->GetAssembler()-> // NOLINT
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<CodeGeneratorMIPS*>(codegen)->GetAssembler()-> // NOLINT
#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kMipsPointerSize, x).Int32Value()
class BoundsCheckSlowPathMIPS : public SlowPathCodeMIPS {
@@ -501,8 +501,8 @@
}
#undef __
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<MipsAssembler*>(GetAssembler())-> // NOLINT
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<MipsAssembler*>(GetAssembler())-> // NOLINT
#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kMipsPointerSize, x).Int32Value()
void CodeGeneratorMIPS::Finalize(CodeAllocator* allocator) {
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index fe1fddc..4e7a272 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -102,8 +102,8 @@
return Mips64ReturnLocation(type);
}
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<CodeGeneratorMIPS64*>(codegen)->GetAssembler()-> // NOLINT
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<CodeGeneratorMIPS64*>(codegen)->GetAssembler()-> // NOLINT
#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kMips64PointerSize, x).Int32Value()
class BoundsCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 {
@@ -427,8 +427,8 @@
}
#undef __
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<Mips64Assembler*>(GetAssembler())-> // NOLINT
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<Mips64Assembler*>(GetAssembler())-> // NOLINT
#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kMips64PointerSize, x).Int32Value()
void CodeGeneratorMIPS64::Finalize(CodeAllocator* allocator) {
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index a85cd54..7a561bb 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -47,8 +47,8 @@
static constexpr int kFakeReturnRegister = Register(8);
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<X86Assembler*>(codegen->GetAssembler())-> // NOLINT
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<X86Assembler*>(codegen->GetAssembler())-> // NOLINT
#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kX86PointerSize, x).Int32Value()
class NullCheckSlowPathX86 : public SlowPathCode {
@@ -729,8 +729,8 @@
};
#undef __
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<X86Assembler*>(GetAssembler())-> /* NOLINT */
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<X86Assembler*>(GetAssembler())-> // NOLINT
inline Condition X86Condition(IfCondition cond) {
switch (cond) {
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index e001363..cf01a79 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -51,8 +51,8 @@
static constexpr int kC2ConditionMask = 0x400;
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<X86_64Assembler*>(codegen->GetAssembler())-> // NOLINT
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<X86_64Assembler*>(codegen->GetAssembler())-> // NOLINT
#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kX86_64PointerSize, x).Int32Value()
class NullCheckSlowPathX86_64 : public SlowPathCode {
@@ -748,8 +748,8 @@
};
#undef __
-// NOLINT on __ macro to suppress wrong warning/fix from clang-tidy.
-#define __ down_cast<X86_64Assembler*>(GetAssembler())-> // NOLINT
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<X86_64Assembler*>(GetAssembler())-> // NOLINT
inline Condition X86_64IntegerCondition(IfCondition cond) {
switch (cond) {
diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h
index ff0bbaf..86548e1 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -680,6 +680,8 @@
virtual void vpushd(DRegister reg, int nregs, Condition cond = AL) = 0;
virtual void vpops(SRegister reg, int nregs, Condition cond = AL) = 0;
virtual void vpopd(DRegister reg, int nregs, Condition cond = AL) = 0;
+ virtual void vldmiad(Register base_reg, DRegister reg, int nregs, Condition cond = AL) = 0;
+ virtual void vstmiad(Register base_reg, DRegister reg, int nregs, Condition cond = AL) = 0;
// Branch instructions.
virtual void b(Label* label, Condition cond = AL) = 0;
diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc
index 6f9d5f3..b8eb60c 100644
--- a/compiler/utils/arm/assembler_arm32.cc
+++ b/compiler/utils/arm/assembler_arm32.cc
@@ -1106,6 +1106,18 @@
}
+void Arm32Assembler::vldmiad(Register, DRegister, int, Condition) {
+ LOG(FATAL) << "Unimplemented.";
+ UNREACHABLE();
+}
+
+
+void Arm32Assembler::vstmiad(Register, DRegister, int, Condition) {
+ LOG(FATAL) << "Unimplemented.";
+ UNREACHABLE();
+}
+
+
void Arm32Assembler::EmitVPushPop(uint32_t reg, int nregs, bool push, bool dbl, Condition cond) {
CHECK_NE(cond, kNoCondition);
CHECK_GT(nregs, 0);
diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h
index 044eaa1..0cb6b17 100644
--- a/compiler/utils/arm/assembler_arm32.h
+++ b/compiler/utils/arm/assembler_arm32.h
@@ -212,6 +212,8 @@
void vpushd(DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
void vpops(SRegister reg, int nregs, Condition cond = AL) OVERRIDE;
void vpopd(DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
+ void vldmiad(Register base_reg, DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
+ void vstmiad(Register base_reg, DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
// Branch instructions.
void b(Label* label, Condition cond = AL) OVERRIDE;
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index ee69698..ebdfc98 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -3020,9 +3020,49 @@
}
+void Thumb2Assembler::vldmiad(Register base_reg, DRegister reg, int nregs, Condition cond) {
+ int32_t rest = B23;
+ EmitVLdmOrStm(rest,
+ static_cast<uint32_t>(reg),
+ nregs,
+ base_reg,
+ /*is_load*/ true,
+ /*dbl*/ true,
+ cond);
+}
+
+
+void Thumb2Assembler::vstmiad(Register base_reg, DRegister reg, int nregs, Condition cond) {
+ int32_t rest = B23;
+ EmitVLdmOrStm(rest,
+ static_cast<uint32_t>(reg),
+ nregs,
+ base_reg,
+ /*is_load*/ false,
+ /*dbl*/ true,
+ cond);
+}
+
+
void Thumb2Assembler::EmitVPushPop(uint32_t reg, int nregs, bool push, bool dbl, Condition cond) {
+ int32_t rest = B21 | (push ? B24 : B23);
+ EmitVLdmOrStm(rest, reg, nregs, SP, /*is_load*/ !push, dbl, cond);
+}
+
+
+void Thumb2Assembler::EmitVLdmOrStm(int32_t rest,
+ uint32_t reg,
+ int nregs,
+ Register rn,
+ bool is_load,
+ bool dbl,
+ Condition cond) {
CheckCondition(cond);
+ DCHECK_GT(nregs, 0);
+ DCHECK_LE(reg + nregs, 32u);
+ DCHECK(!dbl || (nregs <= 16));
+
uint32_t D;
uint32_t Vd;
if (dbl) {
@@ -3034,14 +3074,17 @@
D = reg & 1;
Vd = (reg >> 1) & 15U /* 0b1111 */;
}
- int32_t encoding = B27 | B26 | B21 | B19 | B18 | B16 |
- B11 | B9 |
- (dbl ? B8 : 0) |
- (push ? B24 : (B23 | B20)) |
- 14U /* 0b1110 */ << 28 |
- nregs << (dbl ? 1 : 0) |
- D << 22 |
- Vd << 12;
+
+ int32_t encoding = rest |
+ 14U /* 0b1110 */ << 28 |
+ B27 | B26 | B11 | B9 |
+ (is_load ? B20 : 0) |
+ static_cast<int16_t>(rn) << 16 |
+ D << 22 |
+ Vd << 12 |
+ (dbl ? B8 : 0) |
+ nregs << (dbl ? 1 : 0);
+
Emit32(encoding);
}
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index 1c1c98b..13f3bec 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -258,6 +258,8 @@
void vpushd(DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
void vpops(SRegister reg, int nregs, Condition cond = AL) OVERRIDE;
void vpopd(DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
+ void vldmiad(Register base_reg, DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
+ void vstmiad(Register base_reg, DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
// Branch instructions.
void b(Label* label, Condition cond = AL);
@@ -748,6 +750,14 @@
SRegister sn,
SRegister sm);
+ void EmitVLdmOrStm(int32_t rest,
+ uint32_t reg,
+ int nregs,
+ Register rn,
+ bool is_load,
+ bool dbl,
+ Condition cond);
+
void EmitVFPddd(Condition cond,
int32_t opcode,
DRegister dd,
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 3ca3714..d0799d6 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -1611,4 +1611,46 @@
DriverStr(expected, "LoadFromShiftedRegOffset");
}
+TEST_F(AssemblerThumb2Test, VStmLdmPushPop) {
+ // Different D register numbers are used here, to test register encoding.
+ // Source register number is encoded as M:Vm, destination register number is encoded as D:Vd,
+ // For source and destination registers which use D0..D15, the M bit and D bit should be 0.
+ // For source and destination registers which use D16..D32, the M bit and D bit should be 1.
+ // Different data types (signed and unsigned) are also tested.
+ __ vstmiad(arm::R0, arm::D0, 4);
+ __ vldmiad(arm::R1, arm::D9, 5);
+ __ vpopd(arm::D0, 4);
+ __ vpushd(arm::D9, 5);
+ __ vpops(arm::S0, 4);
+ __ vpushs(arm::S9, 5);
+ __ vpushs(arm::S16, 5);
+ __ vpushd(arm::D0, 16);
+ __ vpushd(arm::D1, 15);
+ __ vpushd(arm::D8, 16);
+ __ vpushd(arm::D31, 1);
+ __ vpushs(arm::S0, 32);
+ __ vpushs(arm::S1, 31);
+ __ vpushs(arm::S16, 16);
+ __ vpushs(arm::S31, 1);
+
+ std::string expected =
+ "vstmia r0, {d0 - d3}\n"
+ "vldmia r1, {d9 - d13}\n"
+ "vpop {d0 - d3}\n"
+ "vpush {d9 - d13}\n"
+ "vpop {s0 - s3}\n"
+ "vpush {s9 - s13}\n"
+ "vpush {s16 - s20}\n"
+ "vpush {d0 - d15}\n"
+ "vpush {d1 - d15}\n"
+ "vpush {d8 - d23}\n"
+ "vpush {d31}\n"
+ "vpush {s0 - s31}\n"
+ "vpush {s1 - s31}\n"
+ "vpush {s16 - s31}\n"
+ "vpush {s31}\n";
+
+ DriverStr(expected, "VStmLdmPushPop");
+}
+
} // namespace art
diff --git a/compiler/utils/arm/jni_macro_assembler_arm.cc b/compiler/utils/arm/jni_macro_assembler_arm.cc
index c039816..af5ebb4 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm.cc
@@ -243,14 +243,16 @@
}
void ArmJNIMacroAssembler::LoadRef(ManagedRegister mdest,
- ManagedRegister base,
+ ManagedRegister mbase,
MemberOffset offs,
bool unpoison_reference) {
+ ArmManagedRegister base = mbase.AsArm();
ArmManagedRegister dst = mdest.AsArm();
- CHECK(dst.IsCoreRegister() && dst.IsCoreRegister()) << dst;
+ CHECK(base.IsCoreRegister()) << base;
+ CHECK(dst.IsCoreRegister()) << dst;
__ LoadFromOffset(kLoadWord,
dst.AsCoreRegister(),
- base.AsArm().AsCoreRegister(),
+ base.AsCoreRegister(),
offs.Int32Value());
if (unpoison_reference) {
__ MaybeUnpoisonHeapReference(dst.AsCoreRegister());
@@ -263,13 +265,16 @@
__ LoadFromOffset(kLoadWord, dst.AsCoreRegister(), SP, src.Int32Value());
}
-void ArmJNIMacroAssembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base,
- Offset offs) {
+void ArmJNIMacroAssembler::LoadRawPtr(ManagedRegister mdest,
+ ManagedRegister mbase,
+ Offset offs) {
+ ArmManagedRegister base = mbase.AsArm();
ArmManagedRegister dst = mdest.AsArm();
- CHECK(dst.IsCoreRegister() && dst.IsCoreRegister()) << dst;
+ CHECK(base.IsCoreRegister()) << base;
+ CHECK(dst.IsCoreRegister()) << dst;
__ LoadFromOffset(kLoadWord,
dst.AsCoreRegister(),
- base.AsArm().AsCoreRegister(),
+ base.AsCoreRegister(),
offs.Int32Value());
}
@@ -530,8 +535,9 @@
// TODO: not validating references.
}
-void ArmJNIMacroAssembler::Call(ManagedRegister mbase, Offset offset,
- ManagedRegister mscratch) {
+void ArmJNIMacroAssembler::Call(ManagedRegister mbase,
+ Offset offset,
+ ManagedRegister mscratch) {
ArmManagedRegister base = mbase.AsArm();
ArmManagedRegister scratch = mscratch.AsArm();
CHECK(base.IsCoreRegister()) << base;
diff --git a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
index 47fb59b..3e687a7 100644
--- a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
+++ b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
@@ -288,21 +288,27 @@
}
void X86_64JNIMacroAssembler::LoadRef(ManagedRegister mdest,
- ManagedRegister base,
+ ManagedRegister mbase,
MemberOffset offs,
bool unpoison_reference) {
+ X86_64ManagedRegister base = mbase.AsX86_64();
X86_64ManagedRegister dest = mdest.AsX86_64();
- CHECK(dest.IsCpuRegister() && dest.IsCpuRegister());
- __ movl(dest.AsCpuRegister(), Address(base.AsX86_64().AsCpuRegister(), offs));
+ CHECK(base.IsCpuRegister());
+ CHECK(dest.IsCpuRegister());
+ __ movl(dest.AsCpuRegister(), Address(base.AsCpuRegister(), offs));
if (unpoison_reference) {
__ MaybeUnpoisonHeapReference(dest.AsCpuRegister());
}
}
-void X86_64JNIMacroAssembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base, Offset offs) {
+void X86_64JNIMacroAssembler::LoadRawPtr(ManagedRegister mdest,
+ ManagedRegister mbase,
+ Offset offs) {
+ X86_64ManagedRegister base = mbase.AsX86_64();
X86_64ManagedRegister dest = mdest.AsX86_64();
- CHECK(dest.IsCpuRegister() && dest.IsCpuRegister());
- __ movq(dest.AsCpuRegister(), Address(base.AsX86_64().AsCpuRegister(), offs));
+ CHECK(base.IsCpuRegister());
+ CHECK(dest.IsCpuRegister());
+ __ movq(dest.AsCpuRegister(), Address(base.AsCpuRegister(), offs));
}
void X86_64JNIMacroAssembler::LoadRawPtrFromThread(ManagedRegister mdest, ThreadOffset64 offs) {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index eb11f6d..cfcfe1c 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1576,7 +1576,7 @@
IsAppImage(),
image_classes_.release(),
compiled_classes_.release(),
- /* compiled_methods */ nullptr,
+ compiled_methods_.release(),
thread_count_,
dump_stats_,
dump_passes_,
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 2f8b113..b31eaf6 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -164,6 +164,7 @@
offsets.cc \
os_linux.cc \
parsed_options.cc \
+ plugin.cc \
primitive.cc \
quick_exception_handler.cc \
quick/inline_method_analyser.cc \
@@ -177,6 +178,7 @@
thread.cc \
thread_list.cc \
thread_pool.cc \
+ ti/agent.cc \
trace.cc \
transaction.cc \
type_lookup_table.cc \
@@ -370,6 +372,7 @@
stack.h \
thread.h \
thread_state.h \
+ ti/agent.h \
verifier/method_verifier.h
LIBOPENJDKJVM_SRC_FILES := openjdkjvm/OpenjdkJvm.cc
@@ -419,7 +422,7 @@
endif
ifneq ($(4),libart)
ifneq ($(4),libopenjdkjvm)
- $$(error expected libart of libopenjdkjvm for argument 4, received $(4))
+ $$(error expected libart or libopenjdkjvm for argument 4, received $(4))
endif
endif
diff --git a/runtime/arch/instruction_set.cc b/runtime/arch/instruction_set.cc
index 81ca010..b35e088 100644
--- a/runtime/arch/instruction_set.cc
+++ b/runtime/arch/instruction_set.cc
@@ -18,6 +18,7 @@
// Explicitly include our own elf.h to avoid Linux and other dependencies.
#include "../elf.h"
+#include "base/bit_utils.h"
#include "globals.h"
namespace art {
@@ -113,14 +114,44 @@
}
}
-static constexpr size_t kDefaultStackOverflowReservedBytes = 16 * KB;
-static constexpr size_t kMipsStackOverflowReservedBytes = kDefaultStackOverflowReservedBytes;
-static constexpr size_t kMips64StackOverflowReservedBytes = kDefaultStackOverflowReservedBytes;
+#if !defined(ART_STACK_OVERFLOW_GAP_arm) || !defined(ART_STACK_OVERFLOW_GAP_arm64) || \
+ !defined(ART_STACK_OVERFLOW_GAP_mips) || !defined(ART_STACK_OVERFLOW_GAP_mips64) || \
+ !defined(ART_STACK_OVERFLOW_GAP_x86) || !defined(ART_STACK_OVERFLOW_GAP_x86_64)
+#error "Missing defines for stack overflow gap"
+#endif
-static constexpr size_t kArmStackOverflowReservedBytes = 8 * KB;
-static constexpr size_t kArm64StackOverflowReservedBytes = 8 * KB;
-static constexpr size_t kX86StackOverflowReservedBytes = 8 * KB;
-static constexpr size_t kX86_64StackOverflowReservedBytes = 8 * KB;
+static constexpr size_t kArmStackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_arm;
+static constexpr size_t kArm64StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_arm64;
+static constexpr size_t kMipsStackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_mips;
+static constexpr size_t kMips64StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_mips64;
+static constexpr size_t kX86StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_x86;
+static constexpr size_t kX86_64StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_x86_64;
+
+static_assert(IsAligned<kPageSize>(kArmStackOverflowReservedBytes), "ARM gap not page aligned");
+static_assert(IsAligned<kPageSize>(kArm64StackOverflowReservedBytes), "ARM64 gap not page aligned");
+static_assert(IsAligned<kPageSize>(kMipsStackOverflowReservedBytes), "Mips gap not page aligned");
+static_assert(IsAligned<kPageSize>(kMips64StackOverflowReservedBytes),
+ "Mips64 gap not page aligned");
+static_assert(IsAligned<kPageSize>(kX86StackOverflowReservedBytes), "X86 gap not page aligned");
+static_assert(IsAligned<kPageSize>(kX86_64StackOverflowReservedBytes),
+ "X86_64 gap not page aligned");
+
+#if !defined(ART_FRAME_SIZE_LIMIT)
+#error "ART frame size limit missing"
+#endif
+
+// TODO: Should we require an extra page (RoundUp(SIZE) + kPageSize)?
+static_assert(ART_FRAME_SIZE_LIMIT < kArmStackOverflowReservedBytes, "Frame size limit too large");
+static_assert(ART_FRAME_SIZE_LIMIT < kArm64StackOverflowReservedBytes,
+ "Frame size limit too large");
+static_assert(ART_FRAME_SIZE_LIMIT < kMipsStackOverflowReservedBytes,
+ "Frame size limit too large");
+static_assert(ART_FRAME_SIZE_LIMIT < kMips64StackOverflowReservedBytes,
+ "Frame size limit too large");
+static_assert(ART_FRAME_SIZE_LIMIT < kX86StackOverflowReservedBytes,
+ "Frame size limit too large");
+static_assert(ART_FRAME_SIZE_LIMIT < kX86_64StackOverflowReservedBytes,
+ "Frame size limit too large");
size_t GetStackOverflowReservedBytes(InstructionSet isa) {
switch (isa) {
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index ac8f523..8a4a334 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -910,7 +910,20 @@
END_MACRO
// Generate the allocation entrypoints for each allocator.
-GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR
+GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_REGION_TLAB_ALLOCATORS
+// Comment out allocators that have x86_64 specific asm.
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB)
+// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB)
// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc).
DEFINE_FUNCTION art_quick_alloc_object_rosalloc
@@ -1003,6 +1016,14 @@
MACRO1(ALLOC_OBJECT_TLAB_FAST_PATH, slowPathLabel)
testl %edx, %edx // Check null class
jz RAW_VAR(slowPathLabel)
+ ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH(RAW_VAR(slowPathLabel))
+END_MACRO
+
+// The common fast path code for art_quick_alloc_object_resolved_region_tlab.
+//
+// RDI: type_idx, RSI: ArtMethod*, RDX/EDX: the class, RAX: return value.
+// RCX: scratch, r8: Thread::Current().
+MACRO1(ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH, slowPathLabel)
// Check class status.
cmpl LITERAL(MIRROR_CLASS_STATUS_INITIALIZED), MIRROR_CLASS_STATUS_OFFSET(%rdx)
jne RAW_VAR(slowPathLabel)
@@ -1014,26 +1035,73 @@
// kAccClassIsFinalizable
testl LITERAL(ACCESS_FLAGS_CLASS_IS_FINALIZABLE), MIRROR_CLASS_ACCESS_FLAGS_OFFSET(%rdx)
jnz RAW_VAR(slowPathLabel)
- movq %gs:THREAD_SELF_OFFSET, %r8 // r8 = thread
- movq THREAD_LOCAL_END_OFFSET(%r8), %rax // Load thread_local_end.
- subq THREAD_LOCAL_POS_OFFSET(%r8), %rax // Compute the remaining buffer size.
- movl MIRROR_CLASS_OBJECT_SIZE_OFFSET(%rdx), %ecx // Load the object size.
- cmpq %rax, %rcx // Check if it fits. OK to do this
- // before rounding up the object size
- // assuming the buf size alignment.
+ ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH(RAW_VAR(slowPathLabel))
+END_MACRO
+
+// The fast path code for art_quick_alloc_object_initialized_region_tlab.
+//
+// RDI: type_idx, RSI: ArtMethod*, RDX/EDX: the class, RAX: return value.
+// RCX: scratch, r8: Thread::Current().
+MACRO1(ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH, slowPathLabel)
+ movq %gs:THREAD_SELF_OFFSET, %r8 // r8 = thread
+ movl MIRROR_CLASS_OBJECT_SIZE_OFFSET(%rdx), %ecx // Load the object size.
+ movq THREAD_LOCAL_POS_OFFSET(%r8), %rax
+ leaq OBJECT_ALIGNMENT_MASK(%rax, %rcx), %rcx // Add size to pos, note that these
+ // are both 32 bit ints, overflow
+ // will cause the add to be past the
+ // end of the thread local region.
+ // Also sneak in alignment mask add.
+ andq LITERAL(OBJECT_ALIGNMENT_MASK_TOGGLED64), %rcx // Align the size by 8. (addr + 7) &
+ // ~7.
+ cmpq THREAD_LOCAL_END_OFFSET(%r8), %rcx // Check if it fits.
ja RAW_VAR(slowPathLabel)
- addl LITERAL(OBJECT_ALIGNMENT_MASK), %ecx // Align the size by 8. (addr + 7) & ~7.
- andl LITERAL(OBJECT_ALIGNMENT_MASK_TOGGLED), %ecx
- movq THREAD_LOCAL_POS_OFFSET(%r8), %rax // Load thread_local_pos
- // as allocated object.
- addq %rax, %rcx // Add the object size.
- movq %rcx, THREAD_LOCAL_POS_OFFSET(%r8) // Update thread_local_pos.
- addq LITERAL(1), THREAD_LOCAL_OBJECTS_OFFSET(%r8) // Increase thread_local_objects.
- // Store the class pointer in the header.
- // No fence needed for x86.
+ movq %rcx, THREAD_LOCAL_POS_OFFSET(%r8) // Update thread_local_pos.
+ addq LITERAL(1), THREAD_LOCAL_OBJECTS_OFFSET(%r8) // Increase thread_local_objects.
+ // Store the class pointer in the
+ // header.
+ // No fence needed for x86.
POISON_HEAP_REF edx
movl %edx, MIRROR_OBJECT_CLASS_OFFSET(%rax)
- ret // Fast path succeeded.
+ ret // Fast path succeeded.
+END_MACRO
+
+// The fast path code for art_quick_alloc_array_region_tlab.
+// Inputs: RDI: uint32_t type_idx, RSI: int32_t component_count, RDX: ArtMethod* method
+// Temps: RCX: the class, r8, r9
+// Output: RAX: return value.
+MACRO1(ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED, slowPathLabel)
+ movq %rcx, %r8 // Save class for later
+ movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%rcx), %ecx // Load component type.
+ UNPOISON_HEAP_REF ecx
+ movl MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET(%rcx), %ecx // Load primitive type.
+ shrq LITERAL(PRIMITIVE_TYPE_SIZE_SHIFT_SHIFT), %rcx // Get component size shift.
+ movq %rsi, %r9
+ salq %cl, %r9 // Calculate array count shifted.
+ // Add array header + alignment rounding.
+ addq LITERAL(MIRROR_INT_ARRAY_DATA_OFFSET + OBJECT_ALIGNMENT_MASK), %r9
+ // Add 4 extra bytes if we are doing a long array.
+ addq LITERAL(1), %rcx
+ andq LITERAL(4), %rcx
+ addq %rcx, %r9
+ movq %gs:THREAD_SELF_OFFSET, %rcx // rcx = thread
+#if MIRROR_LONG_ARRAY_DATA_OFFSET != MIRROR_INT_ARRAY_DATA_OFFSET + 4
+#error Long array data offset must be 4 greater than int array data offset.
+#endif
+ // Mask out the unaligned part to make sure we are 8 byte aligned.
+ andq LITERAL(OBJECT_ALIGNMENT_MASK_TOGGLED64), %r9
+ movq THREAD_LOCAL_POS_OFFSET(%rcx), %rax
+ addq %rax, %r9
+ cmpq THREAD_LOCAL_END_OFFSET(%rcx), %r9 // Check if it fits.
+ ja RAW_VAR(slowPathLabel)
+ movq %r9, THREAD_LOCAL_POS_OFFSET(%rcx) // Update thread_local_pos.
+ addq LITERAL(1), THREAD_LOCAL_OBJECTS_OFFSET(%rcx) // Increase thread_local_objects.
+ // Store the class pointer in the
+ // header.
+ // No fence needed for x86.
+ POISON_HEAP_REF ecx
+ movl %r8d, MIRROR_OBJECT_CLASS_OFFSET(%rax)
+ movl %esi, MIRROR_ARRAY_LENGTH_OFFSET(%rax)
+ ret // Fast path succeeded.
END_MACRO
// The common slow path code for art_quick_alloc_object_tlab and art_quick_alloc_object_region_tlab.
@@ -1046,6 +1114,16 @@
RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception
END_MACRO
+// The slow path code for art_quick_alloc_array_region_tlab.
+MACRO1(ALLOC_ARRAY_TLAB_SLOW_PATH, cxx_name)
+ SETUP_SAVE_REFS_ONLY_FRAME // save ref containing registers for GC
+ // Outgoing argument set up
+ movq %gs:THREAD_SELF_OFFSET, %rcx // pass Thread::Current()
+ call CALLVAR(cxx_name) // cxx_name(arg0, arg1, arg2, Thread*)
+ RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception
+END_MACRO
+
// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB).
DEFINE_FUNCTION art_quick_alloc_object_tlab
// Fast path tlab allocation.
@@ -1065,6 +1143,82 @@
ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeTLAB
END_FUNCTION art_quick_alloc_object_tlab
+// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB).
+DEFINE_FUNCTION art_quick_alloc_array_region_tlab
+ // Fast path region tlab allocation.
+ // RDI: uint32_t type_idx, RSI: int32_t component_count, RDX: ArtMethod*
+ // RCX: klass, R8, R9: free. RAX: return val.
+#if !defined(USE_READ_BARRIER)
+ int3
+ int3
+#endif
+ movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rdx), %rcx // Load dex cache resolved types array
+ movl 0(%rcx, %rdi, COMPRESSED_REFERENCE_SIZE), %ecx // Load the class
+ // Null check so that we can load the lock word.
+ testl %ecx, %ecx
+ jz .Lart_quick_alloc_array_region_tlab_slow_path
+
+ cmpl LITERAL(0), %gs:THREAD_IS_GC_MARKING_OFFSET
+ jne .Lart_quick_alloc_array_region_tlab_class_load_read_barrier_marking
+.Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path_exit:
+ ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED .Lart_quick_alloc_array_region_tlab_slow_path
+.Lart_quick_alloc_array_region_tlab_class_load_read_barrier_marking:
+ // Check the mark bit, if it is 1 return.
+ testl LITERAL(LOCK_WORD_MARK_BIT_MASK_SHIFTED), MIRROR_OBJECT_LOCK_WORD_OFFSET(%ecx)
+ jnz .Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path_exit
+.Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path:
+ // The read barrier slow path. Mark the class.
+ PUSH rdi
+ PUSH rsi
+ PUSH rdx
+ // Outgoing argument set up
+ movq %rcx, %rdi // Pass the class as the first param.
+ call SYMBOL(artReadBarrierMark) // cxx_name(mirror::Object* obj)
+ movq %rax, %rcx
+ POP rdx
+ POP rsi
+ POP rdi
+ jmp .Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path_exit
+.Lart_quick_alloc_array_region_tlab_slow_path:
+ ALLOC_ARRAY_TLAB_SLOW_PATH artAllocArrayFromCodeRegionTLAB
+END_FUNCTION art_quick_alloc_array_region_tlab
+
+// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB).
+DEFINE_FUNCTION art_quick_alloc_array_resolved_region_tlab
+ // Fast path region tlab allocation.
+ // RDI: mirror::Class* klass, RSI: int32_t component_count, RDX: ArtMethod*
+ // RCX: mirror::Class* klass, R8, R9: free. RAX: return val.
+#if !defined(USE_READ_BARRIER)
+ int3
+ int3
+#endif
+ movq %rdi, %rcx
+ // Already resolved, no null check.
+ cmpl LITERAL(0), %gs:THREAD_IS_GC_MARKING_OFFSET
+ jne .Lart_quick_alloc_array_resolved_region_tlab_class_load_read_barrier_marking
+.Lart_quick_alloc_array_resolved_region_tlab_class_load_read_barrier_slow_path_exit:
+ ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED .Lart_quick_alloc_array_resolved_region_tlab_slow_path
+.Lart_quick_alloc_array_resolved_region_tlab_class_load_read_barrier_marking:
+ // Check the mark bit, if it is 1 return.
+ testl LITERAL(LOCK_WORD_MARK_BIT_MASK_SHIFTED), MIRROR_OBJECT_LOCK_WORD_OFFSET(%ecx)
+ jnz .Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path_exit
+.Lart_quick_alloc_array_resolved_region_tlab_class_load_read_barrier_slow_path:
+ // The read barrier slow path. Mark the class.
+ PUSH rdi
+ PUSH rsi
+ PUSH rdx
+ // Outgoing argument set up
+ movq %rcx, %rdi // Pass the class as the first param.
+ call SYMBOL(artReadBarrierMark) // cxx_name(mirror::Object* obj)
+ movq %rax, %rcx
+ POP rdx
+ POP rsi
+ POP rdi
+ jmp .Lart_quick_alloc_array_resolved_region_tlab_class_load_read_barrier_slow_path_exit
+.Lart_quick_alloc_array_resolved_region_tlab_slow_path:
+ ALLOC_ARRAY_TLAB_SLOW_PATH artAllocArrayFromCodeResolvedRegionTLAB
+END_FUNCTION art_quick_alloc_array_resolved_region_tlab
+
// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB).
DEFINE_FUNCTION art_quick_alloc_object_region_tlab
// Fast path region tlab allocation.
@@ -1074,29 +1228,30 @@
int3
int3
#endif
- // Might need a special macro since rsi and edx is 32b/64b mismatched.
movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rsi), %rdx // Load dex cache resolved types array
- // Might need to break down into multiple instructions to get the base address in a register.
- // Load the class
- movl 0(%rdx, %rdi, COMPRESSED_REFERENCE_SIZE), %edx
- cmpl LITERAL(0), %gs:THREAD_IS_GC_MARKING_OFFSET
- jz .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit
+ movl 0(%rdx, %rdi, COMPRESSED_REFERENCE_SIZE), %edx // Load the class
// Null check so that we can load the lock word.
testl %edx, %edx
- jz .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit
- // Check the mark bit, if it is 1 return.
- testl LITERAL(LOCK_WORD_MARK_BIT_MASK_SHIFTED), MIRROR_OBJECT_LOCK_WORD_OFFSET(%edx)
- jz .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path
+ jz .Lart_quick_alloc_object_region_tlab_slow_path
+ // Test if the GC is marking.
+ cmpl LITERAL(0), %gs:THREAD_IS_GC_MARKING_OFFSET
+ jne .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_marking
.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit:
ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_region_tlab_slow_path
+.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_marking:
+ // Check the mark bit, if it is 1 avoid the read barrier.
+ testl LITERAL(LOCK_WORD_MARK_BIT_MASK_SHIFTED), MIRROR_OBJECT_LOCK_WORD_OFFSET(%edx)
+ jnz .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit
.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path:
// The read barrier slow path. Mark the class.
PUSH rdi
PUSH rsi
+ subq LITERAL(8), %rsp // 16 byte alignment
// Outgoing argument set up
movq %rdx, %rdi // Pass the class as the first param.
call SYMBOL(artReadBarrierMark) // cxx_name(mirror::Object* obj)
movq %rax, %rdx
+ addq LITERAL(8), %rsp
POP rsi
POP rdi
jmp .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit
@@ -1104,6 +1259,77 @@
ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeRegionTLAB
END_FUNCTION art_quick_alloc_object_region_tlab
+// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB).
+DEFINE_FUNCTION art_quick_alloc_object_resolved_region_tlab
+ // Fast path region tlab allocation.
+ // RDI: mirror::Class* klass, RSI: ArtMethod*
+ // RDX, RCX, R8, R9: free. RAX: return val.
+#if !defined(USE_READ_BARRIER)
+ int3
+ int3
+#endif
+ movq %rdi, %rdx
+ cmpl LITERAL(0), %gs:THREAD_IS_GC_MARKING_OFFSET
+ jne .Lart_quick_alloc_object_resolved_region_tlab_class_load_read_barrier_marking
+.Lart_quick_alloc_object_resolved_region_tlab_class_load_read_barrier_slow_path_exit:
+ ALLOC_OBJECT_RESOLVED_TLAB_FAST_PATH .Lart_quick_alloc_object_resolved_region_tlab_slow_path
+.Lart_quick_alloc_object_resolved_region_tlab_class_load_read_barrier_marking:
+ // Check the mark bit, if it is 1 avoid the read barrier.
+ testl LITERAL(LOCK_WORD_MARK_BIT_MASK_SHIFTED), MIRROR_OBJECT_LOCK_WORD_OFFSET(%edx)
+ jnz .Lart_quick_alloc_object_resolved_region_tlab_class_load_read_barrier_slow_path_exit
+.Lart_quick_alloc_object_resolved_region_tlab_class_load_read_barrier_slow_path:
+ // The read barrier slow path. Mark the class.
+ PUSH rdi
+ PUSH rsi
+ subq LITERAL(8), %rsp // 16 byte alignment
+ // Outgoing argument set up
+ movq %rdx, %rdi // Pass the class as the first param.
+ call SYMBOL(artReadBarrierMark) // cxx_name(mirror::Object* obj)
+ movq %rax, %rdx
+ addq LITERAL(8), %rsp
+ POP rsi
+ POP rdi
+ jmp .Lart_quick_alloc_object_resolved_region_tlab_class_load_read_barrier_slow_path_exit
+.Lart_quick_alloc_object_resolved_region_tlab_slow_path:
+ ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeResolvedRegionTLAB
+END_FUNCTION art_quick_alloc_object_resolved_region_tlab
+
+// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB).
+DEFINE_FUNCTION art_quick_alloc_object_initialized_region_tlab
+ // Fast path region tlab allocation.
+ // RDI: mirror::Class* klass, RSI: ArtMethod*
+ // RDX, RCX, R8, R9: free. RAX: return val.
+#if !defined(USE_READ_BARRIER)
+ int3
+ int3
+#endif
+ // Might need a special macro since rsi and edx is 32b/64b mismatched.
+ movq %rdi, %rdx
+ cmpl LITERAL(0), %gs:THREAD_IS_GC_MARKING_OFFSET
+ jne .Lart_quick_alloc_object_initialized_region_tlab_class_load_read_barrier_marking
+.Lart_quick_alloc_object_initialized_region_tlab_class_load_read_barrier_slow_path_exit:
+ ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH .Lart_quick_alloc_object_initialized_region_tlab_slow_path
+.Lart_quick_alloc_object_initialized_region_tlab_class_load_read_barrier_marking:
+ // Check the mark bit, if it is 1 avoid the read barrier.
+ testl LITERAL(LOCK_WORD_MARK_BIT_MASK_SHIFTED), MIRROR_OBJECT_LOCK_WORD_OFFSET(%edx)
+ jnz .Lart_quick_alloc_object_initialized_region_tlab_class_load_read_barrier_slow_path
+.Lart_quick_alloc_object_initialized_region_tlab_class_load_read_barrier_slow_path:
+ // The read barrier slow path. Mark the class.
+ PUSH rdi
+ PUSH rsi
+ subq LITERAL(8), %rsp // 16 byte alignment
+ // Outgoing argument set up
+ movq %rdx, %rdi // Pass the class as the first param.
+ call SYMBOL(artReadBarrierMark) // cxx_name(mirror::Object* obj)
+ movq %rax, %rdx
+ addq LITERAL(8), %rsp
+ POP rsi
+ POP rdi
+ jmp .Lart_quick_alloc_object_initialized_region_tlab_class_load_read_barrier_slow_path_exit
+.Lart_quick_alloc_object_initialized_region_tlab_slow_path:
+ ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeInitializedRegionTLAB
+END_FUNCTION art_quick_alloc_object_initialized_region_tlab
+
ONE_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
ONE_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index 6323eee..ac21a3f 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -57,6 +57,7 @@
bool verifier;
bool image;
bool systrace_lock_logging; // Enabled with "-verbose:sys-locks".
+ bool agents;
};
// Global log verbosity setting, initialized by InitLogging.
diff --git a/runtime/base/macros.h b/runtime/base/macros.h
index 3c43253..5a50247 100644
--- a/runtime/base/macros.h
+++ b/runtime/base/macros.h
@@ -75,7 +75,7 @@
ALWAYS_INLINE void* operator new(size_t, void* ptr) noexcept { return ptr; } \
ALWAYS_INLINE void operator delete(void*, void*) noexcept { } \
private: \
- void* operator new(size_t) = delete // NOLINT
+ void* operator new(size_t) = delete // NOLINT
// The arraysize(arr) macro returns the # of elements in an array arr.
// The expression is a compile-time constant, and therefore can be
@@ -135,13 +135,13 @@
#define ARRAYSIZE_UNSAFE(a) \
((sizeof(a) / sizeof(*(a))) / static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
-#define SIZEOF_MEMBER(t, f) sizeof((reinterpret_cast<t*>(4096))->f) // NOLINT
+#define SIZEOF_MEMBER(t, f) sizeof((reinterpret_cast<t*>(4096))->f) // NOLINT
#define OFFSETOF_MEMBER(t, f) \
- (reinterpret_cast<uintptr_t>(&reinterpret_cast<t*>(16)->f) - static_cast<uintptr_t>(16u)) // NOLINT
+ (reinterpret_cast<uintptr_t>(&reinterpret_cast<t*>(16)->f) - static_cast<uintptr_t>(16u)) // NOLINT
#define OFFSETOF_MEMBERPTR(t, f) \
- (reinterpret_cast<uintptr_t>(&(reinterpret_cast<t*>(16)->*f)) - static_cast<uintptr_t>(16)) // NOLINT
+ (reinterpret_cast<uintptr_t>(&(reinterpret_cast<t*>(16)->*f)) - static_cast<uintptr_t>(16)) // NOLINT
#define PACKED(x) __attribute__ ((__aligned__(x), __packed__))
diff --git a/runtime/experimental_flags.h b/runtime/experimental_flags.h
index fde1a5f..7faa2dc 100644
--- a/runtime/experimental_flags.h
+++ b/runtime/experimental_flags.h
@@ -26,6 +26,8 @@
// The actual flag values.
enum {
kNone = 0x0000,
+ kAgents = 0x0001, // 0b00000001
+ kRuntimePlugins = 0x0002, // 0b00000010
};
constexpr ExperimentalFlags() : value_(0x0000) {}
@@ -61,9 +63,19 @@
uint32_t value_;
};
-inline std::ostream& operator<<(std::ostream& stream,
- const ExperimentalFlags& e ATTRIBUTE_UNUSED) {
- stream << "kNone";
+inline std::ostream& operator<<(std::ostream& stream, const ExperimentalFlags& e) {
+ bool started = false;
+ if (e & ExperimentalFlags::kAgents) {
+ stream << (started ? "|" : "") << "kAgents";
+ started = true;
+ }
+ if (e & ExperimentalFlags::kRuntimePlugins) {
+ stream << (started ? "|" : "") << "kRuntimePlugins";
+ started = true;
+ }
+ if (!started) {
+ stream << "kNone";
+ }
return stream;
}
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 88fbf78..b574c3b 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -230,6 +230,9 @@
total_wait_time_(0),
verify_object_mode_(kVerifyObjectModeDisabled),
disable_moving_gc_count_(0),
+ semi_space_collector_(nullptr),
+ mark_compact_collector_(nullptr),
+ concurrent_copying_collector_(nullptr),
is_running_on_memory_tool_(Runtime::Current()->IsRunningOnMemoryTool()),
use_tlab_(use_tlab),
main_space_backup_(nullptr),
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index c644cde..2401bec 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -48,7 +48,7 @@
static const size_t kWeakGlobalsInitial = 16; // Arbitrary.
static const size_t kWeakGlobalsMax = 51200; // Arbitrary sanity check. (Must fit in 16 bits.)
-static bool IsBadJniVersion(int version) {
+bool JavaVMExt::IsBadJniVersion(int version) {
// We don't support JNI_VERSION_1_1. These are the only other valid versions.
return version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 && version != JNI_VERSION_1_6;
}
@@ -344,13 +344,6 @@
}
static jint GetEnv(JavaVM* vm, void** env, jint version) {
- // GetEnv always returns a JNIEnv* for the most current supported JNI version,
- // and unlike other calls that take a JNI version doesn't care if you supply
- // JNI_VERSION_1_1, which we don't otherwise support.
- if (IsBadJniVersion(version) && version != JNI_VERSION_1_1) {
- LOG(ERROR) << "Bad JNI version passed to GetEnv: " << version;
- return JNI_EVERSION;
- }
if (vm == nullptr || env == nullptr) {
return JNI_ERR;
}
@@ -359,8 +352,8 @@
*env = nullptr;
return JNI_EDETACHED;
}
- *env = thread->GetJniEnv();
- return JNI_OK;
+ JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
+ return raw_vm->HandleGetEnv(env, version);
}
private:
@@ -388,7 +381,7 @@
const char* thread_name = nullptr;
jobject thread_group = nullptr;
if (args != nullptr) {
- if (IsBadJniVersion(args->version)) {
+ if (JavaVMExt::IsBadJniVersion(args->version)) {
LOG(ERROR) << "Bad JNI version passed to "
<< (as_daemon ? "AttachCurrentThreadAsDaemon" : "AttachCurrentThread") << ": "
<< args->version;
@@ -436,7 +429,8 @@
weak_globals_lock_("JNI weak global reference table lock", kJniWeakGlobalsLock),
weak_globals_(kWeakGlobalsInitial, kWeakGlobalsMax, kWeakGlobal),
allow_accessing_weak_globals_(true),
- weak_globals_add_condition_("weak globals add condition", weak_globals_lock_) {
+ weak_globals_add_condition_("weak globals add condition", weak_globals_lock_),
+ env_hooks_() {
functions = unchecked_functions_;
SetCheckJniEnabled(runtime_options.Exists(RuntimeArgumentMap::CheckJni));
}
@@ -444,6 +438,26 @@
JavaVMExt::~JavaVMExt() {
}
+jint JavaVMExt::HandleGetEnv(/*out*/void** env, jint version) {
+ for (GetEnvHook hook : env_hooks_) {
+ jint res = hook(this, env, version);
+ if (res == JNI_OK) {
+ return JNI_OK;
+ } else if (res != JNI_EVERSION) {
+ LOG(ERROR) << "Error returned from a plugin GetEnv handler! " << res;
+ return res;
+ }
+ }
+ LOG(ERROR) << "Bad JNI version passed to GetEnv: " << version;
+ return JNI_EVERSION;
+}
+
+// Add a hook to handle getting environments from the GetEnv call.
+void JavaVMExt::AddEnvironmentHook(GetEnvHook hook) {
+ CHECK(hook != nullptr) << "environment hooks shouldn't be null!";
+ env_hooks_.push_back(hook);
+}
+
void JavaVMExt::JniAbort(const char* jni_function_name, const char* msg) {
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
@@ -866,7 +880,7 @@
if (version == JNI_ERR) {
StringAppendF(error_msg, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
- } else if (IsBadJniVersion(version)) {
+ } else if (JavaVMExt::IsBadJniVersion(version)) {
StringAppendF(error_msg, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
path.c_str(), version);
// It's unwise to call dlclose() here, but we can mark it
@@ -939,7 +953,7 @@
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
ScopedTrace trace(__FUNCTION__);
const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args);
- if (IsBadJniVersion(args->version)) {
+ if (JavaVMExt::IsBadJniVersion(args->version)) {
LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version;
return JNI_EVERSION;
}
diff --git a/runtime/java_vm_ext.h b/runtime/java_vm_ext.h
index 3d055cd..ed9d3ab 100644
--- a/runtime/java_vm_ext.h
+++ b/runtime/java_vm_ext.h
@@ -36,6 +36,10 @@
class Runtime;
struct RuntimeArgumentMap;
+class JavaVMExt;
+// Hook definition for runtime plugins.
+using GetEnvHook = jint (*)(JavaVMExt* vm, /*out*/void** new_env, jint version);
+
class JavaVMExt : public JavaVM {
public:
JavaVMExt(Runtime* runtime, const RuntimeArgumentMap& runtime_options);
@@ -171,6 +175,12 @@
void TrimGlobals() SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!globals_lock_);
+ jint HandleGetEnv(/*out*/void** env, jint version);
+
+ void AddEnvironmentHook(GetEnvHook hook);
+
+ static bool IsBadJniVersion(int version);
+
private:
// Return true if self can currently access weak globals.
bool MayAccessWeakGlobalsUnlocked(Thread* self) const SHARED_REQUIRES(Locks::mutator_lock_);
@@ -215,6 +225,9 @@
Atomic<bool> allow_accessing_weak_globals_;
ConditionVariable weak_globals_add_condition_ GUARDED_BY(weak_globals_lock_);
+ // TODO Maybe move this to Runtime.
+ std::vector<GetEnvHook> env_hooks_;
+
DISALLOW_COPY_AND_ASSIGN(JavaVMExt);
};
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
index 1ee1611..40efc89 100644
--- a/runtime/jni_env_ext.cc
+++ b/runtime/jni_env_ext.cc
@@ -45,6 +45,20 @@
return in->locals.IsValid();
}
+jint JNIEnvExt::GetEnvHandler(JavaVMExt* vm, /*out*/void** env, jint version) {
+ UNUSED(vm);
+ // GetEnv always returns a JNIEnv* for the most current supported JNI version,
+ // and unlike other calls that take a JNI version doesn't care if you supply
+ // JNI_VERSION_1_1, which we don't otherwise support.
+ if (JavaVMExt::IsBadJniVersion(version) && version != JNI_VERSION_1_1) {
+ return JNI_EVERSION;
+ }
+ Thread* thread = Thread::Current();
+ CHECK(thread != nullptr);
+ *env = thread->GetJniEnv();
+ return JNI_OK;
+}
+
JNIEnvExt* JNIEnvExt::Create(Thread* self_in, JavaVMExt* vm_in) {
std::unique_ptr<JNIEnvExt> ret(new JNIEnvExt(self_in, vm_in));
if (CheckLocalsValid(ret.get())) {
diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h
index d4accc3..ac287d4 100644
--- a/runtime/jni_env_ext.h
+++ b/runtime/jni_env_ext.h
@@ -54,6 +54,8 @@
static Offset LocalRefCookieOffset(size_t pointer_size);
static Offset SelfOffset(size_t pointer_size);
+ static jint GetEnvHandler(JavaVMExt* vm, /*out*/void** out, jint version);
+
jobject NewLocalRef(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_);
void DeleteLocalRef(jobject obj) SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index c7e4f8b..174da79 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -23,6 +23,7 @@
#include "gc/heap.h"
#include "monitor.h"
#include "runtime.h"
+#include "ti/agent.h"
#include "trace.h"
#include "utils.h"
@@ -90,6 +91,13 @@
.Define({"-Xrunjdwp:_", "-agentlib:jdwp=_"})
.WithType<JDWP::JdwpOptions>()
.IntoKey(M::JdwpOptions)
+ // TODO Re-enable -agentlib: once I have a good way to transform the values.
+ // .Define("-agentlib:_")
+ // .WithType<std::vector<ti::Agent>>().AppendValues()
+ // .IntoKey(M::AgentLib)
+ .Define("-agentpath:_")
+ .WithType<std::vector<ti::Agent>>().AppendValues()
+ .IntoKey(M::AgentPath)
.Define("-Xms_")
.WithType<MemoryKiB>()
.IntoKey(M::MemoryInitialSize)
@@ -289,6 +297,9 @@
.IntoKey(M::Experimental)
.Define("-Xforce-nb-testing")
.IntoKey(M::ForceNativeBridge)
+ .Define("-Xplugin:_")
+ .WithType<std::vector<Plugin>>().AppendValues()
+ .IntoKey(M::Plugins)
.Ignore({
"-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
"-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
@@ -583,6 +594,42 @@
args.Set(M::HeapGrowthLimit, args.GetOrDefault(M::MemoryMaximumSize));
}
+ if (args.GetOrDefault(M::Experimental) & ExperimentalFlags::kRuntimePlugins) {
+ LOG(WARNING) << "Experimental runtime plugin support has been enabled. No guarantees are made "
+ << "about stability or usage of this plugin support. Use at your own risk. Do "
+ << "not attempt to write shipping code that relies on the implementation of "
+ << "runtime plugins.";
+ } else if (!args.GetOrDefault(M::Plugins).empty()) {
+ LOG(WARNING) << "Experimental runtime plugin support has not been enabled. Ignored options: ";
+ for (auto& op : args.GetOrDefault(M::Plugins)) {
+ LOG(WARNING) << " -plugin:" << op.GetLibrary();
+ }
+ }
+
+ if (args.GetOrDefault(M::Experimental) & ExperimentalFlags::kAgents) {
+ LOG(WARNING) << "Experimental runtime agent support has been enabled. No guarantees are made "
+ << "the completeness, accuracy, reliability, or stability of the agent "
+ << "implementation. Use at your own risk. Do not attempt to write shipping code "
+ << "that relies on the implementation of any part of this api.";
+ } else if (!args.GetOrDefault(M::AgentLib).empty() || !args.GetOrDefault(M::AgentPath).empty()) {
+ LOG(WARNING) << "agent support has not been enabled. Enable experimental agent "
+ << " support with '-XExperimental:agent'. Ignored options are:";
+ for (auto op : args.GetOrDefault(M::AgentLib)) {
+ if (op.HasArgs()) {
+ LOG(WARNING) << " -agentlib:" << op.GetName() << "=" << op.GetArgs();
+ } else {
+ LOG(WARNING) << " -agentlib:" << op.GetName();
+ }
+ }
+ for (auto op : args.GetOrDefault(M::AgentPath)) {
+ if (op.HasArgs()) {
+ LOG(WARNING) << " -agentpath:" << op.GetName() << "=" << op.GetArgs();
+ } else {
+ LOG(WARNING) << " -agentpath:" << op.GetName();
+ }
+ }
+ }
+
*runtime_options = std::move(args);
return true;
}
@@ -627,6 +674,11 @@
UsageMessage(stream, " -showversion\n");
UsageMessage(stream, " -help\n");
UsageMessage(stream, " -agentlib:jdwp=options\n");
+ // TODO add back in once -agentlib actually does something.
+ // UsageMessage(stream, " -agentlib:library=options (Experimental feature, "
+ // "requires -Xexperimental:agent, some features might not be supported)\n");
+ UsageMessage(stream, " -agentpath:library_path=options (Experimental feature, "
+ "requires -Xexperimental:agent, some features might not be supported)\n");
UsageMessage(stream, "\n");
UsageMessage(stream, "The following extended options are supported:\n");
@@ -703,6 +755,12 @@
UsageMessage(stream, " -X[no]image-dex2oat (Whether to create and use a boot image)\n");
UsageMessage(stream, " -Xno-dex-file-fallback "
"(Don't fall back to dex files without oat files)\n");
+ UsageMessage(stream, " -Xplugin:<library.so> "
+ "(Load a runtime plugin, requires -Xexperimental:runtime-plugins)\n");
+ UsageMessage(stream, " -Xexperimental:runtime-plugins"
+ "(Enable new and experimental agent support)\n");
+ UsageMessage(stream, " -Xexperimental:agents"
+ "(Enable new and experimental agent support)\n");
UsageMessage(stream, "\n");
UsageMessage(stream, "The following previously supported Dalvik options are ignored:\n");
diff --git a/runtime/plugin.cc b/runtime/plugin.cc
new file mode 100644
index 0000000..481b1ca
--- /dev/null
+++ b/runtime/plugin.cc
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#include "plugin.h"
+
+#include <dlfcn.h>
+#include "base/stringprintf.h"
+#include "base/logging.h"
+
+namespace art {
+
+const char* PLUGIN_INITIALIZATION_FUNCTION_NAME = "ArtPlugin_Initialize";
+const char* PLUGIN_DEINITIALIZATION_FUNCTION_NAME = "ArtPlugin_Deinitialize";
+
+Plugin::Plugin(const Plugin& other) : library_(other.library_), dlopen_handle_(nullptr) {
+ if (other.IsLoaded()) {
+ std::string err;
+ Load(&err);
+ }
+}
+
+bool Plugin::Load(/*out*/std::string* error_msg) {
+ DCHECK(!IsLoaded());
+ void* res = dlopen(library_.c_str(), RTLD_LAZY);
+ if (res == nullptr) {
+ *error_msg = StringPrintf("dlopen failed: %s", dlerror());
+ return false;
+ }
+ // Get the initializer function
+ PluginInitializationFunction init = reinterpret_cast<PluginInitializationFunction>(
+ dlsym(res, PLUGIN_INITIALIZATION_FUNCTION_NAME));
+ if (init != nullptr) {
+ if (!init()) {
+ dlclose(res);
+ *error_msg = StringPrintf("Initialization of plugin failed");
+ return false;
+ }
+ } else {
+ LOG(WARNING) << this << " does not include an initialization function";
+ }
+ dlopen_handle_ = res;
+ return true;
+}
+
+bool Plugin::Unload() {
+ DCHECK(IsLoaded());
+ bool ret = true;
+ void* handle = dlopen_handle_;
+ PluginDeinitializationFunction deinit = reinterpret_cast<PluginDeinitializationFunction>(
+ dlsym(handle, PLUGIN_DEINITIALIZATION_FUNCTION_NAME));
+ if (deinit != nullptr) {
+ if (!deinit()) {
+ LOG(WARNING) << this << " failed deinitialization";
+ ret = false;
+ }
+ } else {
+ LOG(WARNING) << this << " does not include a deinitialization function";
+ }
+ dlopen_handle_ = nullptr;
+ if (dlclose(handle) != 0) {
+ LOG(ERROR) << this << " failed to dlclose: " << dlerror();
+ ret = false;
+ }
+ return ret;
+}
+
+std::ostream& operator<<(std::ostream &os, const Plugin* m) {
+ return os << *m;
+}
+
+std::ostream& operator<<(std::ostream &os, Plugin const& m) {
+ return os << "Plugin { library=\"" << m.library_ << "\", handle=" << m.dlopen_handle_ << " }";
+}
+
+} // namespace art
diff --git a/runtime/plugin.h b/runtime/plugin.h
new file mode 100644
index 0000000..18f3977
--- /dev/null
+++ b/runtime/plugin.h
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_PLUGIN_H_
+#define ART_RUNTIME_PLUGIN_H_
+
+#include <string>
+#include "base/logging.h"
+
+namespace art {
+
+// This function is loaded from the plugin (if present) and called during runtime initialization.
+// By the time this has been called the runtime has been fully initialized but not other native
+// libraries have been loaded yet. Failure to initialize is considered a fatal error.
+// TODO might want to give initialization function some arguments
+using PluginInitializationFunction = bool (*)();
+using PluginDeinitializationFunction = bool (*)();
+
+// A class encapsulating a plugin. There is no stable plugin ABI or API and likely never will be.
+// TODO Might want to put some locking in this but ATM we only load these at initialization in a
+// single-threaded fashion so not much need
+class Plugin {
+ public:
+ static Plugin Create(std::string lib) {
+ return Plugin(lib);
+ }
+
+ bool IsLoaded() const {
+ return dlopen_handle_ != nullptr;
+ }
+
+ const std::string& GetLibrary() const {
+ return library_;
+ }
+
+ bool Load(/*out*/std::string* error_msg);
+ bool Unload();
+
+
+ ~Plugin() {
+ if (IsLoaded() && !Unload()) {
+ LOG(ERROR) << "Error unloading " << this;
+ }
+ }
+
+ Plugin(const Plugin& other);
+
+ // Create move constructor for putting this in a list
+ Plugin(Plugin&& other)
+ : library_(other.library_),
+ dlopen_handle_(other.dlopen_handle_) {
+ other.dlopen_handle_ = nullptr;
+ }
+
+ private:
+ explicit Plugin(std::string library) : library_(library), dlopen_handle_(nullptr) { }
+
+ std::string library_;
+ void* dlopen_handle_;
+
+ friend std::ostream& operator<<(std::ostream &os, Plugin const& m);
+};
+
+std::ostream& operator<<(std::ostream &os, Plugin const& m);
+std::ostream& operator<<(std::ostream &os, const Plugin* m);
+
+} // namespace art
+
+#endif // ART_RUNTIME_PLUGIN_H_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 68fa0d3..ddcfb6d 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -130,6 +130,7 @@
#include "signal_set.h"
#include "thread.h"
#include "thread_list.h"
+#include "ti/agent.h"
#include "trace.h"
#include "transaction.h"
#include "utils.h"
@@ -281,6 +282,16 @@
jit_->StopProfileSaver();
}
+ // TODO Maybe do some locking.
+ for (auto& agent : agents_) {
+ agent.Unload();
+ }
+
+ // TODO Maybe do some locking
+ for (auto& plugin : plugins_) {
+ plugin.Unload();
+ }
+
// Make sure our internal threads are dead before we start tearing down things they're using.
Dbg::StopJdwp();
delete signal_catcher_;
@@ -960,6 +971,16 @@
experimental_flags_ = runtime_options.GetOrDefault(Opt::Experimental);
is_low_memory_mode_ = runtime_options.Exists(Opt::LowMemoryMode);
+ if (experimental_flags_ & ExperimentalFlags::kRuntimePlugins) {
+ plugins_ = runtime_options.ReleaseOrDefault(Opt::Plugins);
+ }
+ if (experimental_flags_ & ExperimentalFlags::kAgents) {
+ agents_ = runtime_options.ReleaseOrDefault(Opt::AgentPath);
+ // TODO Add back in -agentlib
+ // for (auto lib : runtime_options.ReleaseOrDefault(Opt::AgentLib)) {
+ // agents_.push_back(lib);
+ // }
+ }
XGcOption xgc_option = runtime_options.GetOrDefault(Opt::GcOption);
heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize),
runtime_options.GetOrDefault(Opt::HeapGrowthLimit),
@@ -1084,6 +1105,10 @@
java_vm_ = new JavaVMExt(this, runtime_options);
+ // Add the JniEnv handler.
+ // TODO Refactor this stuff.
+ java_vm_->AddEnvironmentHook(JNIEnvExt::GetEnvHandler);
+
Thread::Startup();
// ClassLinker needs an attached thread, but we can't fully attach a thread without creating
@@ -1200,6 +1225,16 @@
pre_allocated_NoClassDefFoundError_ = GcRoot<mirror::Throwable>(self->GetException());
self->ClearException();
+ // Runtime initialization is largely done now.
+ // We load plugins first since that can modify the runtime state slightly.
+ // Load all plugins
+ for (auto& plugin : plugins_) {
+ std::string err;
+ if (!plugin.Load(&err)) {
+ LOG(FATAL) << plugin << " failed to load: " << err;
+ }
+ }
+
// Look for a native bridge.
//
// The intended flow here is, in the case of a running system:
@@ -1232,6 +1267,20 @@
is_native_bridge_loaded_ = LoadNativeBridge(native_bridge_file_name);
}
+ // Startup agents
+ // TODO Maybe we should start a new thread to run these on. Investigate RI behavior more.
+ for (auto& agent : agents_) {
+ // TODO Check err
+ int res = 0;
+ std::string err = "";
+ ti::Agent::LoadError result = agent.Load(&res, &err);
+ if (result == ti::Agent::kInitializationError) {
+ LOG(FATAL) << "Unable to initialize agent!";
+ } else if (result != ti::Agent::kNoError) {
+ LOG(ERROR) << "Unable to load an agent: " << err;
+ }
+ }
+
VLOG(startup) << "Runtime::Init exiting";
return true;
diff --git a/runtime/runtime.h b/runtime/runtime.h
index c971646..6da60f2 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -63,6 +63,9 @@
class String;
class Throwable;
} // namespace mirror
+namespace ti {
+ class Agent;
+} // namespace ti
namespace verifier {
class MethodVerifier;
enum class VerifyMode : int8_t;
@@ -80,6 +83,7 @@
class MonitorPool;
class NullPointerHandler;
class OatFileManager;
+class Plugin;
struct RuntimeArgumentMap;
class SignalCatcher;
class StackOverflowHandler;
@@ -698,6 +702,9 @@
std::string class_path_string_;
std::vector<std::string> properties_;
+ std::vector<ti::Agent> agents_;
+ std::vector<Plugin> plugins_;
+
// The default stack size for managed threads created by the runtime.
size_t default_stack_size_;
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index b95dfad..146afc7 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -117,7 +117,10 @@
RUNTIME_OPTIONS_KEY (Unit, NoDexFileFallback)
RUNTIME_OPTIONS_KEY (std::string, CpuAbiList)
RUNTIME_OPTIONS_KEY (std::string, Fingerprint)
-RUNTIME_OPTIONS_KEY (ExperimentalFlags, Experimental, ExperimentalFlags::kNone) // -Xexperimental:{none}
+RUNTIME_OPTIONS_KEY (ExperimentalFlags, Experimental, ExperimentalFlags::kNone) // -Xexperimental:{none, agents}
+RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>, AgentLib) // -agentlib:<libname>=<options>, Requires -Xexperimental:agents
+RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>, AgentPath) // -agentpath:<libname>=<options>, Requires -Xexperimental:agents
+RUNTIME_OPTIONS_KEY (std::vector<Plugin>, Plugins) // -Xplugin:<library> Requires -Xexperimental:runtime-plugins
// Not parse-able from command line, but can be provided explicitly.
// (Do not add anything here that is defined in ParsedOptions::MakeParser)
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 4647d67..dd7e531 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -1050,7 +1050,7 @@
inline_info_encoding = *reinterpret_cast<const InlineInfoEncoding*>(ptr);
ptr += sizeof(InlineInfoEncoding);
} else {
- inline_info_encoding = InlineInfoEncoding{}; // NOLINT.
+ inline_info_encoding = InlineInfoEncoding{}; // NOLINT.
}
header_size = dchecked_integral_cast<uint8_t>(ptr - reinterpret_cast<const uint8_t*>(data));
}
diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc
new file mode 100644
index 0000000..41a21f7
--- /dev/null
+++ b/runtime/ti/agent.cc
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+#include "agent.h"
+#include "java_vm_ext.h"
+#include "runtime.h"
+
+namespace art {
+namespace ti {
+
+const char* AGENT_ON_LOAD_FUNCTION_NAME = "Agent_OnLoad";
+const char* AGENT_ON_ATTACH_FUNCTION_NAME = "Agent_OnAttach";
+const char* AGENT_ON_UNLOAD_FUNCTION_NAME = "Agent_OnUnload";
+
+Agent Agent::Create(std::string arg) {
+ size_t eq = arg.find_first_of('=');
+ if (eq == std::string::npos) {
+ return Agent(arg, "");
+ } else {
+ return Agent(arg.substr(0, eq), arg.substr(eq + 1, arg.length()));
+ }
+}
+
+// TODO We need to acquire some locks probably.
+Agent::LoadError Agent::Load(/*out*/jint* call_res, /*out*/ std::string* error_msg) {
+ DCHECK(call_res != nullptr);
+ DCHECK(error_msg != nullptr);
+ if (IsStarted()) {
+ *error_msg = StringPrintf("the agent at %s has already been started!", name_.c_str());
+ VLOG(agents) << "err: " << *error_msg;
+ return kAlreadyStarted;
+ }
+ LoadError err = DoDlOpen(error_msg);
+ if (err != kNoError) {
+ VLOG(agents) << "err: " << *error_msg;
+ return err;
+ }
+ if (onload_ == nullptr) {
+ *error_msg = StringPrintf("Unable to start agent %s: No Agent_OnLoad function found",
+ name_.c_str());
+ VLOG(agents) << "err: " << *error_msg;
+ return kLoadingError;
+ }
+ // TODO Need to do some checks that we are at a good spot etc.
+ *call_res = onload_(static_cast<JavaVM*>(Runtime::Current()->GetJavaVM()),
+ args_.c_str(),
+ nullptr);
+ if (*call_res != 0) {
+ *error_msg = StringPrintf("Initialization of %s returned non-zero value of %d",
+ name_.c_str(), *call_res);
+ VLOG(agents) << "err: " << *error_msg;
+ return kInitializationError;
+ } else {
+ return kNoError;
+ }
+}
+
+Agent::LoadError Agent::DoDlOpen(/*out*/std::string* error_msg) {
+ DCHECK(error_msg != nullptr);
+ dlopen_handle_ = dlopen(name_.c_str(), RTLD_LAZY);
+ if (dlopen_handle_ == nullptr) {
+ *error_msg = StringPrintf("Unable to dlopen %s: %s", name_.c_str(), dlerror());
+ return kLoadingError;
+ }
+
+ onload_ = reinterpret_cast<AgentOnLoadFunction>(dlsym(dlopen_handle_,
+ AGENT_ON_LOAD_FUNCTION_NAME));
+ if (onload_ == nullptr) {
+ VLOG(agents) << "Unable to find 'Agent_OnLoad' symbol in " << this;
+ }
+ onattach_ = reinterpret_cast<AgentOnAttachFunction>(dlsym(dlopen_handle_,
+ AGENT_ON_ATTACH_FUNCTION_NAME));
+ if (onattach_ == nullptr) {
+ VLOG(agents) << "Unable to find 'Agent_OnAttach' symbol in " << this;
+ }
+ onunload_= reinterpret_cast<AgentOnUnloadFunction>(dlsym(dlopen_handle_,
+ AGENT_ON_UNLOAD_FUNCTION_NAME));
+ if (onunload_ == nullptr) {
+ VLOG(agents) << "Unable to find 'Agent_OnUnload' symbol in " << this;
+ }
+ return kNoError;
+}
+
+// TODO Lock some stuff probably.
+void Agent::Unload() {
+ if (dlopen_handle_ != nullptr) {
+ if (onunload_ != nullptr) {
+ onunload_(Runtime::Current()->GetJavaVM());
+ }
+ dlclose(dlopen_handle_);
+ dlopen_handle_ = nullptr;
+ } else {
+ VLOG(agents) << this << " is not currently loaded!";
+ }
+}
+
+Agent::Agent(const Agent& other)
+ : name_(other.name_),
+ args_(other.args_),
+ dlopen_handle_(other.dlopen_handle_),
+ onload_(other.onload_),
+ onattach_(other.onattach_),
+ onunload_(other.onunload_) {
+ if (other.dlopen_handle_ != nullptr) {
+ dlopen(other.name_.c_str(), 0);
+ }
+}
+
+Agent::~Agent() {
+ if (dlopen_handle_ != nullptr) {
+ dlclose(dlopen_handle_);
+ }
+}
+
+std::ostream& operator<<(std::ostream &os, const Agent* m) {
+ return os << *m;
+}
+
+std::ostream& operator<<(std::ostream &os, Agent const& m) {
+ return os << "Agent { name=\"" << m.name_ << "\", args=\"" << m.args_ << "\", handle="
+ << m.dlopen_handle_ << " }";
+}
+
+} // namespace ti
+} // namespace art
diff --git a/runtime/ti/agent.h b/runtime/ti/agent.h
new file mode 100644
index 0000000..521e21e
--- /dev/null
+++ b/runtime/ti/agent.h
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_TI_AGENT_H_
+#define ART_RUNTIME_TI_AGENT_H_
+
+#include <dlfcn.h>
+#include <jni.h> // for jint, JavaVM* etc declarations
+
+#include "base/stringprintf.h"
+#include "runtime.h"
+#include "utils.h"
+
+namespace art {
+namespace ti {
+
+using AgentOnLoadFunction = jint (*)(JavaVM*, const char*, void*);
+using AgentOnAttachFunction = jint (*)(JavaVM*, const char*, void*);
+using AgentOnUnloadFunction = void (*)(JavaVM*);
+
+class Agent {
+ public:
+ enum LoadError {
+ kNoError, // No error occurred..
+ kAlreadyStarted, // The agent has already been loaded.
+ kLoadingError, // dlopen or dlsym returned an error.
+ kInitializationError, // The entrypoint did not return 0. This might require an abort.
+ };
+
+ bool IsStarted() const {
+ return dlopen_handle_ != nullptr;
+ }
+
+ const std::string& GetName() const {
+ return name_;
+ }
+
+ const std::string& GetArgs() const {
+ return args_;
+ }
+
+ bool HasArgs() const {
+ return !GetArgs().empty();
+ }
+
+ // TODO We need to acquire some locks probably.
+ LoadError Load(/*out*/jint* call_res, /*out*/std::string* error_msg);
+
+ // TODO We need to acquire some locks probably.
+ void Unload();
+
+ // Tries to attach the agent using its OnAttach method. Returns true on success.
+ // TODO We need to acquire some locks probably.
+ LoadError Attach(std::string* error_msg) {
+ // TODO
+ *error_msg = "Attach has not yet been implemented!";
+ return kLoadingError;
+ }
+
+ static Agent Create(std::string arg);
+
+ static Agent Create(std::string name, std::string args) {
+ return Agent(name, args);
+ }
+
+ ~Agent();
+
+ // We need move constructor and copy for vectors
+ Agent(const Agent& other);
+
+ Agent(Agent&& other)
+ : name_(other.name_),
+ args_(other.args_),
+ dlopen_handle_(nullptr),
+ onload_(nullptr),
+ onattach_(nullptr),
+ onunload_(nullptr) {
+ other.dlopen_handle_ = nullptr;
+ other.onload_ = nullptr;
+ other.onattach_ = nullptr;
+ other.onunload_ = nullptr;
+ }
+
+ // We don't need an operator=
+ void operator=(const Agent&) = delete;
+
+ private:
+ Agent(std::string name, std::string args)
+ : name_(name),
+ args_(args),
+ dlopen_handle_(nullptr),
+ onload_(nullptr),
+ onattach_(nullptr),
+ onunload_(nullptr) { }
+
+ LoadError DoDlOpen(/*out*/std::string* error_msg);
+
+ const std::string name_;
+ const std::string args_;
+ void* dlopen_handle_;
+
+ // The entrypoints.
+ AgentOnLoadFunction onload_;
+ AgentOnAttachFunction onattach_;
+ AgentOnUnloadFunction onunload_;
+
+ friend std::ostream& operator<<(std::ostream &os, Agent const& m);
+};
+
+std::ostream& operator<<(std::ostream &os, Agent const& m);
+std::ostream& operator<<(std::ostream &os, const Agent* m);
+
+} // namespace ti
+} // namespace art
+
+#endif // ART_RUNTIME_TI_AGENT_H_
+
diff --git a/test/900-hello-plugin/build b/test/900-hello-plugin/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/900-hello-plugin/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-build "$@" --experimental agents
diff --git a/test/900-hello-plugin/expected.txt b/test/900-hello-plugin/expected.txt
new file mode 100644
index 0000000..43db31c
--- /dev/null
+++ b/test/900-hello-plugin/expected.txt
@@ -0,0 +1,8 @@
+ArtPlugin_Initialize called in test 900
+Agent_OnLoad called with options "test_900"
+GetEnvHandler called in test 900
+GetEnvHandler called with version 0x900fffff
+GetEnv returned '900' environment!
+Hello, world!
+Agent_OnUnload called
+ArtPlugin_Deinitialize called in test 900
diff --git a/test/900-hello-plugin/info.txt b/test/900-hello-plugin/info.txt
new file mode 100644
index 0000000..47b15c2
--- /dev/null
+++ b/test/900-hello-plugin/info.txt
@@ -0,0 +1,2 @@
+Tests that agents and plugins are loaded.
+
diff --git a/test/900-hello-plugin/load_unload.cc b/test/900-hello-plugin/load_unload.cc
new file mode 100644
index 0000000..a38cc3d
--- /dev/null
+++ b/test/900-hello-plugin/load_unload.cc
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#include <jni.h>
+#include <stdio.h>
+
+#include "art_method-inl.h"
+#include "base/logging.h"
+#include "base/macros.h"
+
+namespace art {
+
+constexpr jint TEST_900_ENV_VERSION_NUMBER = 0x900FFFFF;
+constexpr uintptr_t ENV_VALUE = 900;
+
+// Allow this library to be used as a plugin too so we can test the stack.
+static jint GetEnvHandler(JavaVMExt* vm ATTRIBUTE_UNUSED, void** new_env, jint version) {
+ printf("%s called in test 900\n", __func__);
+ if (version != TEST_900_ENV_VERSION_NUMBER) {
+ return JNI_EVERSION;
+ }
+ printf("GetEnvHandler called with version 0x%x\n", version);
+ *new_env = reinterpret_cast<void*>(ENV_VALUE);
+ return JNI_OK;
+}
+
+extern "C" bool ArtPlugin_Initialize() {
+ printf("%s called in test 900\n", __func__);
+ Runtime::Current()->GetJavaVM()->AddEnvironmentHook(GetEnvHandler);
+ return true;
+}
+
+extern "C" bool ArtPlugin_Deinitialize() {
+ printf("%s called in test 900\n", __func__);
+ return true;
+}
+
+extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm,
+ char* options,
+ void* reserved ATTRIBUTE_UNUSED) {
+ printf("Agent_OnLoad called with options \"%s\"\n", options);
+ uintptr_t env = 0;
+ jint res = vm->GetEnv(reinterpret_cast<void**>(&env), TEST_900_ENV_VERSION_NUMBER);
+ if (res != JNI_OK) {
+ printf("GetEnv(TEST_900_ENV_VERSION_NUMBER) returned non-zero\n");
+ }
+ printf("GetEnv returned '%" PRIdPTR "' environment!\n", env);
+ return 0;
+}
+
+extern "C" JNIEXPORT void JNICALL Agent_OnUnload(JavaVM* vm ATTRIBUTE_UNUSED) {
+ printf("Agent_OnUnload called\n");
+}
+
+} // namespace art
diff --git a/test/900-hello-plugin/run b/test/900-hello-plugin/run
new file mode 100755
index 0000000..bb9b415
--- /dev/null
+++ b/test/900-hello-plugin/run
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --runtime-option -agentpath:libartagentd.so=test_900 \
+ --android-runtime-option -Xplugin:libartagentd.so
diff --git a/test/900-hello-plugin/src/Main.java b/test/900-hello-plugin/src/Main.java
new file mode 100644
index 0000000..1ef6289
--- /dev/null
+++ b/test/900-hello-plugin/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ }
+}
diff --git a/test/Android.libartagent.mk b/test/Android.libartagent.mk
new file mode 100644
index 0000000..729de3f
--- /dev/null
+++ b/test/Android.libartagent.mk
@@ -0,0 +1,101 @@
+#
+# 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.
+#
+
+
+LOCAL_PATH := $(call my-dir)
+
+include art/build/Android.common_build.mk
+
+LIBARTAGENT_COMMON_SRC_FILES := \
+ 900-hello-plugin/load_unload.cc
+
+# $(1): target or host
+# $(2): debug or <empty>
+define build-libartagent
+ ifneq ($(1),target)
+ ifneq ($(1),host)
+ $$(error expected target or host for argument 1, received $(1))
+ endif
+ endif
+ ifneq ($(2),debug)
+ ifneq ($(2),)
+ $$(error d or empty for argument 2, received $(2))
+ endif
+ suffix := d
+ else
+ suffix :=
+ endif
+
+ art_target_or_host := $(1)
+
+ include $(CLEAR_VARS)
+ LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
+ LOCAL_MODULE := libartagent$$(suffix)
+ ifeq ($$(art_target_or_host),target)
+ LOCAL_MODULE_TAGS := tests
+ endif
+ LOCAL_SRC_FILES := $(LIBARTAGENT_COMMON_SRC_FILES)
+ LOCAL_SHARED_LIBRARIES += libart$$(suffix) libbacktrace libnativehelper
+ LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
+ LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
+ LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.libartagent.mk
+ ifeq ($$(art_target_or_host),target)
+ $(call set-target-local-clang-vars)
+ ifeq ($$(suffix),d)
+ $(call set-target-local-cflags-vars,debug)
+ else
+ $(call set-target-local-cflags-vars,ndebug)
+ endif
+ LOCAL_SHARED_LIBRARIES += libdl
+ LOCAL_MULTILIB := both
+ LOCAL_MODULE_PATH_32 := $(ART_TARGET_TEST_OUT)/$(ART_TARGET_ARCH_32)
+ LOCAL_MODULE_PATH_64 := $(ART_TARGET_TEST_OUT)/$(ART_TARGET_ARCH_64)
+ LOCAL_MODULE_TARGET_ARCH := $(ART_SUPPORTED_ARCH)
+ include $(BUILD_SHARED_LIBRARY)
+ else # host
+ LOCAL_CLANG := $(ART_HOST_CLANG)
+ LOCAL_CFLAGS := $(ART_HOST_CFLAGS)
+ LOCAL_ASFLAGS := $(ART_HOST_ASFLAGS)
+ ifeq ($$(suffix),d)
+ LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS)
+ LOCAL_ASFLAGS += $(ART_HOST_DEBUG_ASFLAGS)
+ else
+ LOCAL_CFLAGS += $(ART_HOST_NON_DEBUG_CFLAGS)
+ LOCAL_ASFLAGS += $(ART_HOST_NON_DEBUG_ASFLAGS)
+ endif
+ LOCAL_LDLIBS := $(ART_HOST_LDLIBS) -ldl -lpthread
+ LOCAL_IS_HOST_MODULE := true
+ LOCAL_MULTILIB := both
+ include $(BUILD_HOST_SHARED_LIBRARY)
+ endif
+
+ # Clear locally used variables.
+ art_target_or_host :=
+ suffix :=
+endef
+
+ifeq ($(ART_BUILD_TARGET),true)
+ $(eval $(call build-libartagent,target,))
+ $(eval $(call build-libartagent,target,debug))
+endif
+ifeq ($(ART_BUILD_HOST),true)
+ $(eval $(call build-libartagent,host,))
+ $(eval $(call build-libartagent,host,debug))
+endif
+
+# Clear locally used variables.
+LOCAL_PATH :=
+LIBARTAGENT_COMMON_SRC_FILES :=
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index bba6f8e..05ac5f4 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -653,6 +653,14 @@
# only once).
TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUTS)
+# Also need libartagent.
+TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libartagent.so
+TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libartagentd.so
+ifdef TARGET_2ND_ARCH
+TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_2ND_ARCH)/libartagent.so
+TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_2ND_ARCH)/libartagentd.so
+endif
+
# Also need libarttest.
TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttestd.so
@@ -671,6 +679,8 @@
# specific version depending on the compiler.
ART_TEST_HOST_RUN_TEST_DEPENDENCIES := \
$(ART_HOST_EXECUTABLES) \
+ $(ART_HOST_OUT_SHARED_LIBRARIES)/libartagent$(ART_HOST_SHLIB_EXTENSION) \
+ $(ART_HOST_OUT_SHARED_LIBRARIES)/libartagentd$(ART_HOST_SHLIB_EXTENSION) \
$(ART_HOST_OUT_SHARED_LIBRARIES)/libarttest$(ART_HOST_SHLIB_EXTENSION) \
$(ART_HOST_OUT_SHARED_LIBRARIES)/libarttestd$(ART_HOST_SHLIB_EXTENSION) \
$(ART_HOST_OUT_SHARED_LIBRARIES)/libnativebridgetest$(ART_HOST_SHLIB_EXTENSION) \
@@ -680,6 +690,8 @@
ifneq ($(HOST_PREFER_32_BIT),true)
ART_TEST_HOST_RUN_TEST_DEPENDENCIES += \
+ $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libartagent$(ART_HOST_SHLIB_EXTENSION) \
+ $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libartagentd$(ART_HOST_SHLIB_EXTENSION) \
$(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libarttest$(ART_HOST_SHLIB_EXTENSION) \
$(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libarttestd$(ART_HOST_SHLIB_EXTENSION) \
$(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libnativebridgetest$(ART_HOST_SHLIB_EXTENSION) \
@@ -1092,5 +1104,9 @@
RUN_TYPES :=
DEBUGGABLE_TYPES :=
-include $(LOCAL_PATH)/Android.libarttest.mk
-include art/test/Android.libnativebridgetest.mk
+MY_LOCAL_PATH := $(LOCAL_PATH)
+include $(MY_LOCAL_PATH)/Android.libartagent.mk
+include $(MY_LOCAL_PATH)/Android.libarttest.mk
+include $(MY_LOCAL_PATH)/Android.libnativebridgetest.mk
+MY_LOCAL_PATH :=
+LOCAL_PATH :=
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index c6c9380..0ea5d52 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -21,6 +21,7 @@
EXPERIMENTAL=""
FALSE_BIN="/system/bin/false"
FLAGS=""
+ANDROID_FLAGS=""
GDB=""
GDB_ARGS=""
GDB_SERVER="gdbserver"
@@ -93,6 +94,11 @@
FLAGS="${FLAGS} -Xcompiler-option $option"
COMPILE_FLAGS="${COMPILE_FLAGS} $option"
shift
+ elif [ "x$1" = "x--android-runtime-option" ]; then
+ shift
+ option="$1"
+ ANDROID_FLAGS="${ANDROID_FLAGS} $option"
+ shift
elif [ "x$1" = "x--runtime-option" ]; then
shift
option="$1"
@@ -233,6 +239,7 @@
done
if [ "$USE_JVM" = "n" ]; then
+ FLAGS="${FLAGS} ${ANDROID_FLAGS}"
for feature in ${EXPERIMENTAL}; do
FLAGS="${FLAGS} -Xexperimental:${feature} -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:${feature}"
COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xexperimental:${feature}"
@@ -469,12 +476,12 @@
adb push $TEST_NAME-ex.jar $DEX_LOCATION >/dev/null 2>&1
fi
- LD_LIBRARY_PATH=
+ LD_LIBRARY_PATH=/data/art-test/$ISA
if [ "$ANDROID_ROOT" != "/system" ]; then
# Current default installation is dalvikvm 64bits and dex2oat 32bits,
# so we can only use LD_LIBRARY_PATH when testing on a local
# installation.
- LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBRARY_DIRECTORY
+ LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBRARY_DIRECTORY:$LD_LIBRARY_PATH
fi
PUBLIC_LIBS=libart.so:libartd.so