summaryrefslogtreecommitdiff
path: root/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'runtime')
-rw-r--r--runtime/Android.mk5
-rw-r--r--runtime/arch/arm/entrypoints_init_arm.cc2
-rw-r--r--runtime/arch/arm/quick_entrypoints_arm.S114
-rw-r--r--runtime/arch/arm64/entrypoints_init_arm64.cc2
-rw-r--r--runtime/arch/arm64/quick_entrypoints_arm64.S80
-rw-r--r--runtime/arch/instruction_set.cc47
-rw-r--r--runtime/arch/x86_64/quick_entrypoints_x86_64.S282
-rw-r--r--runtime/base/logging.h1
-rw-r--r--runtime/base/macros.h8
-rw-r--r--runtime/class_linker.cc17
-rw-r--r--runtime/experimental_flags.h18
-rw-r--r--runtime/gc/heap.cc3
-rw-r--r--runtime/gc/space/image_space.cc34
-rw-r--r--runtime/gc/space/image_space_fs.h171
-rw-r--r--runtime/generated/asm_support_gen.h2
-rw-r--r--runtime/java_vm_ext.cc42
-rw-r--r--runtime/java_vm_ext.h13
-rw-r--r--runtime/jit/profile_saver.cc13
-rw-r--r--runtime/jni_env_ext.cc14
-rw-r--r--runtime/jni_env_ext.h2
-rw-r--r--runtime/parsed_options.cc58
-rw-r--r--runtime/plugin.cc88
-rw-r--r--runtime/plugin.h82
-rw-r--r--runtime/runtime.cc49
-rw-r--r--runtime/runtime.h7
-rw-r--r--runtime/runtime_options.def5
-rw-r--r--runtime/simulator/Android.mk4
-rw-r--r--runtime/simulator/code_simulator_arm64.h4
-rw-r--r--runtime/stack_map.h2
-rw-r--r--runtime/ti/agent.cc138
-rw-r--r--runtime/ti/agent.h130
31 files changed, 1082 insertions, 355 deletions
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 2f8b11361c..b31eaf60d8 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -164,6 +164,7 @@ LIBART_COMMON_SRC_FILES := \
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 @@ LIBART_COMMON_SRC_FILES := \
thread.cc \
thread_list.cc \
thread_pool.cc \
+ ti/agent.cc \
trace.cc \
transaction.cc \
type_lookup_table.cc \
@@ -370,6 +372,7 @@ LIBART_ENUM_OPERATOR_OUT_HEADER_FILES := \
stack.h \
thread.h \
thread_state.h \
+ ti/agent.h \
verifier/method_verifier.h
LIBOPENJDKJVM_SRC_FILES := openjdkjvm/OpenjdkJvm.cc
@@ -419,7 +422,7 @@ define build-runtime-library
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/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index 0e2a6720ae..492a12d02b 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -133,7 +133,7 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
qpoints->pReadBarrierMarkReg09 = art_quick_read_barrier_mark_reg09;
qpoints->pReadBarrierMarkReg10 = art_quick_read_barrier_mark_reg10;
qpoints->pReadBarrierMarkReg11 = art_quick_read_barrier_mark_reg11;
- qpoints->pReadBarrierMarkReg12 = art_quick_read_barrier_mark_reg12;
+ qpoints->pReadBarrierMarkReg12 = nullptr; // Cannot use register 12 (IP) to pass arguments.
qpoints->pReadBarrierMarkReg13 = nullptr; // Cannot use register 13 (SP) to pass arguments.
qpoints->pReadBarrierMarkReg14 = nullptr; // Cannot use register 14 (LR) to pass arguments.
qpoints->pReadBarrierMarkReg15 = nullptr; // Cannot use register 15 (PC) to pass arguments.
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 0fcf866e18..c4ec72685f 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -191,7 +191,7 @@
.cfi_rel_offset r11, 44
.cfi_rel_offset ip, 48
.cfi_rel_offset lr, 52
- vpush {s0-s31} @ 32 words of float args.
+ vpush {d0-d15} @ 32 words of float args.
.cfi_adjust_cfa_offset 128
sub sp, #8 @ 2 words of space, alignment padding and Method*
.cfi_adjust_cfa_offset 8
@@ -210,7 +210,7 @@
.macro RESTORE_SAVE_EVERYTHING_FRAME
add sp, #8 @ rewind sp
.cfi_adjust_cfa_offset -8
- vpop {s0-s31}
+ vpop {d0-d15}
.cfi_adjust_cfa_offset -128
pop {r0-r12, lr} @ 14 words of callee saves
.cfi_restore r0
@@ -1246,9 +1246,15 @@ ENTRY art_quick_alloc_object_region_tlab
ldr r2, [r2, r0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT]
// Read barrier for class load.
ldr r3, [r9, #THREAD_IS_GC_MARKING_OFFSET]
- cbnz r3, .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path
+ cbnz r3, .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:
+ cbz r2, .Lart_quick_alloc_object_region_tlab_slow_path // Null check for loading lock word.
+ // Check lock word for mark bit, if marked do the allocation.
+ ldr r3, [r2, MIRROR_OBJECT_LOCK_WORD_OFFSET]
+ ands r3, #LOCK_WORD_MARK_BIT_MASK_SHIFTED
+ bne .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.
@@ -1817,6 +1823,39 @@ ENTRY art_quick_l2f
pop {pc}
END art_quick_l2f
+.macro CONDITIONAL_CBZ reg, reg_if, dest
+.ifc \reg, \reg_if
+ cbz \reg, \dest
+.endif
+.endm
+
+.macro CONDITIONAL_CMPBZ reg, reg_if, dest
+.ifc \reg, \reg_if
+ cmp \reg, #0
+ beq \dest
+.endif
+.endm
+
+// Use CBZ if the register is in {r0, r7} otherwise compare and branch.
+.macro SMART_CBZ reg, dest
+ CONDITIONAL_CBZ \reg, r0, \dest
+ CONDITIONAL_CBZ \reg, r1, \dest
+ CONDITIONAL_CBZ \reg, r2, \dest
+ CONDITIONAL_CBZ \reg, r3, \dest
+ CONDITIONAL_CBZ \reg, r4, \dest
+ CONDITIONAL_CBZ \reg, r5, \dest
+ CONDITIONAL_CBZ \reg, r6, \dest
+ CONDITIONAL_CBZ \reg, r7, \dest
+ CONDITIONAL_CMPBZ \reg, r8, \dest
+ CONDITIONAL_CMPBZ \reg, r9, \dest
+ CONDITIONAL_CMPBZ \reg, r10, \dest
+ CONDITIONAL_CMPBZ \reg, r11, \dest
+ CONDITIONAL_CMPBZ \reg, r12, \dest
+ CONDITIONAL_CMPBZ \reg, r13, \dest
+ CONDITIONAL_CMPBZ \reg, r14, \dest
+ CONDITIONAL_CMPBZ \reg, r15, \dest
+.endm
+
/*
* Create a function `name` calling the ReadBarrier::Mark routine,
* getting its argument and returning its result through register
@@ -1835,28 +1874,25 @@ END art_quick_l2f
.macro READ_BARRIER_MARK_REG name, reg
ENTRY \name
// Null check so that we can load the lock word.
- cmp \reg, #0
- beq .Lret_rb_\name
- // Check lock word for mark bit, if marked return.
- push {r0}
- ldr r0, [\reg, MIRROR_OBJECT_LOCK_WORD_OFFSET]
- and r0, #LOCK_WORD_MARK_BIT_MASK_SHIFTED
- cbz r0, .Lslow_rb_\name
- // Restore LR and return.
- pop {r0}
- bx lr
+ SMART_CBZ \reg, .Lret_rb_\name
+ // Check lock word for mark bit, if marked return. Use IP for scratch since it is blocked.
+ ldr ip, [\reg, MIRROR_OBJECT_LOCK_WORD_OFFSET]
+ ands ip, #LOCK_WORD_MARK_BIT_MASK_SHIFTED
+ beq .Lslow_rb_\name
+ // Already marked, return right away.
+ bx lr
.Lslow_rb_\name:
- pop {r0}
- push {r0-r4, r9, r12, lr} @ save return address and core caller-save registers
+ push {r0-r5, r9, lr} @ save return address and core caller-save registers
+ @ also save callee save r5 for 16 byte alignment
.cfi_adjust_cfa_offset 32
.cfi_rel_offset r0, 0
.cfi_rel_offset r1, 4
.cfi_rel_offset r2, 8
.cfi_rel_offset r3, 12
.cfi_rel_offset r4, 16
- .cfi_rel_offset r9, 20
- .cfi_rel_offset r12, 24
+ .cfi_rel_offset r5, 20
+ .cfi_rel_offset r9, 24
.cfi_rel_offset lr, 28
vpush {s0-s15} @ save floating-point caller-save registers
.cfi_adjust_cfa_offset 64
@@ -1865,48 +1901,11 @@ ENTRY \name
mov r0, \reg @ pass arg1 - obj from `reg`
.endif
bl artReadBarrierMark @ r0 <- artReadBarrierMark(obj)
-
+ mov ip, r0 @ Save result in IP
vpop {s0-s15} @ restore floating-point registers
.cfi_adjust_cfa_offset -64
- @ If `reg` is a caller-save register, save the result to its
- @ corresponding stack slot; it will be restored by the "pop"
- @ instruction below. Otherwise, move result into `reg`.
- @
- @ (Note that saving `reg` to its stack slot will overwrite the value
- @ previously stored by the "push" instruction above. That is
- @ alright, as in that case we know that `reg` is not a live
- @ register, as it is used to pass the argument and return the result
- @ of this function.)
- .ifc \reg, r0
- PUSH_REG r0, 0 @ copy result to r0's stack location
- .else
- .ifc \reg, r1
- PUSH_REG r0, 4 @ copy result to r1's stack location
- .else
- .ifc \reg, r2
- PUSH_REG r0, 8 @ copy result to r2's stack location
- .else
- .ifc \reg, r3
- PUSH_REG r0, 12 @ copy result to r3's stack location
- .else
- .ifc \reg, r4
- PUSH_REG r0, 16 @ copy result to r4's stack location
- .else
- .ifc \reg, r9
- PUSH_REG r0, 20 @ copy result to r9's stack location
- .else
- .ifc \reg, r12
- PUSH_REG r0, 24 @ copy result to r12's stack location
- .else
- mov \reg, r0 @ return result into `reg`
- .endif
- .endif
- .endif
- .endif
- .endif
- .endif
- .endif
- pop {r0-r4, r9, r12, pc} @ restore caller-save registers and return
+ pop {r0-r5, r9, lr} @ restore caller-save registers
+ mov \reg, ip @ copy result to reg
.Lret_rb_\name:
bx lr
END \name
@@ -1924,4 +1923,3 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg08, r8
READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg09, r9
READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg10, r10
READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg11, r11
-READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg12, r12
diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc
index cc5bf29609..55b09c318c 100644
--- a/runtime/arch/arm64/entrypoints_init_arm64.cc
+++ b/runtime/arch/arm64/entrypoints_init_arm64.cc
@@ -149,7 +149,7 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
qpoints->pReadBarrierMarkReg13 = art_quick_read_barrier_mark_reg13;
qpoints->pReadBarrierMarkReg14 = art_quick_read_barrier_mark_reg14;
qpoints->pReadBarrierMarkReg15 = art_quick_read_barrier_mark_reg15;
- qpoints->pReadBarrierMarkReg16 = art_quick_read_barrier_mark_reg16;
+ qpoints->pReadBarrierMarkReg16 = nullptr; // IP0 is used as a temp by the asm stub.
qpoints->pReadBarrierMarkReg17 = art_quick_read_barrier_mark_reg17;
qpoints->pReadBarrierMarkReg18 = art_quick_read_barrier_mark_reg18;
qpoints->pReadBarrierMarkReg19 = art_quick_read_barrier_mark_reg19;
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index bdad966496..4289cabbc6 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -331,22 +331,23 @@
#endif
// Save FP registers.
- stp d0, d1, [sp, #8]
- stp d2, d3, [sp, #24]
- stp d4, d5, [sp, #40]
- stp d6, d7, [sp, #56]
- stp d8, d9, [sp, #72]
- stp d10, d11, [sp, #88]
- stp d12, d13, [sp, #104]
- stp d14, d15, [sp, #120]
- stp d16, d17, [sp, #136]
- stp d18, d19, [sp, #152]
- stp d20, d21, [sp, #168]
- stp d22, d23, [sp, #184]
- stp d24, d25, [sp, #200]
- stp d26, d27, [sp, #216]
- stp d28, d29, [sp, #232]
- stp d30, d31, [sp, #248]
+ str d0, [sp, #8]
+ stp d1, d2, [sp, #16]
+ stp d3, d4, [sp, #32]
+ stp d5, d6, [sp, #48]
+ stp d7, d8, [sp, #64]
+ stp d9, d10, [sp, #80]
+ stp d11, d12, [sp, #96]
+ stp d13, d14, [sp, #112]
+ stp d15, d16, [sp, #128]
+ stp d17, d18, [sp, #144]
+ stp d19, d20, [sp, #160]
+ stp d21, d22, [sp, #176]
+ stp d23, d24, [sp, #192]
+ stp d25, d26, [sp, #208]
+ stp d27, d28, [sp, #224]
+ stp d29, d30, [sp, #240]
+ str d31, [sp, #256]
// Save core registers.
str x0, [sp, #264]
@@ -430,22 +431,23 @@
.macro RESTORE_SAVE_EVERYTHING_FRAME
// Restore FP registers.
- ldp d0, d1, [sp, #8]
- ldp d2, d3, [sp, #24]
- ldp d4, d5, [sp, #40]
- ldp d6, d7, [sp, #56]
- ldp d8, d9, [sp, #72]
- ldp d10, d11, [sp, #88]
- ldp d12, d13, [sp, #104]
- ldp d14, d15, [sp, #120]
- ldp d16, d17, [sp, #136]
- ldp d18, d19, [sp, #152]
- ldp d20, d21, [sp, #168]
- ldp d22, d23, [sp, #184]
- ldp d24, d25, [sp, #200]
- ldp d26, d27, [sp, #216]
- ldp d28, d29, [sp, #232]
- ldp d30, d31, [sp, #248]
+ ldr d0, [sp, #8]
+ ldp d1, d2, [sp, #16]
+ ldp d3, d4, [sp, #32]
+ ldp d5, d6, [sp, #48]
+ ldp d7, d8, [sp, #64]
+ ldp d9, d10, [sp, #80]
+ ldp d11, d12, [sp, #96]
+ ldp d13, d14, [sp, #112]
+ ldp d15, d16, [sp, #128]
+ ldp d17, d18, [sp, #144]
+ ldp d19, d20, [sp, #160]
+ ldp d21, d22, [sp, #176]
+ ldp d23, d24, [sp, #192]
+ ldp d25, d26, [sp, #208]
+ ldp d27, d28, [sp, #224]
+ ldp d29, d30, [sp, #240]
+ ldr d31, [sp, #256]
// Restore core registers.
ldr x0, [sp, #264]
@@ -1939,10 +1941,13 @@ END art_quick_alloc_object_rosalloc
// (for 64 bit alignment).
and \xTemp0, \xTemp0, #4
add \xTemp1, \xTemp1, \xTemp0
- and \xTemp1, \xTemp1, #OBJECT_ALIGNMENT_MASK_TOGGLED // Round up the object size by the
- // object alignment. (addr + 7) & ~7.
- // Add by 7 is done above.
-
+ and \xTemp1, \xTemp1, #OBJECT_ALIGNMENT_MASK_TOGGLED64 // Apply alignemnt mask
+ // (addr + 7) & ~7. The mask must
+ // be 64 bits to keep high bits in
+ // case of overflow.
+ // Negative sized arrays are handled here since xCount holds a zero extended 32 bit value.
+ // Negative ints become large 64 bit unsigned ints which will always be larger than max signed
+ // 32 bit int. Since the max shift for arrays is 3, it can not become a negative 64 bit int.
cmp \xTemp1, #MIN_LARGE_OBJECT_THRESHOLD // Possibly a large object, go slow
bhs \slowPathLabel // path.
@@ -1956,7 +1961,6 @@ END art_quick_alloc_object_rosalloc
sub \xTemp2, \xTemp2, \xTemp0
cmp \xTemp1, \xTemp2
bhi \slowPathLabel
-
// "Point of no slow path". Won't go to the slow path from here on. OK to clobber x0 and x1.
// Move old thread_local_pos to x0
// for the return value.
@@ -2747,7 +2751,7 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg12, w12, x12
READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg13, w13, x13
READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg14, w14, x14
READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg15, w15, x15
-READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg16, w16, x16
+// READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg16, w16, x16 ip0 is blocked
READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg17, w17, x17
READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg18, w18, x18
READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg19, w19, x19
diff --git a/runtime/arch/instruction_set.cc b/runtime/arch/instruction_set.cc
index 81ca010423..b35e0889e4 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 @@ size_t GetInstructionSetAlignment(InstructionSet isa) {
}
}
-static constexpr size_t kDefaultStackOverflowReservedBytes = 16 * KB;
-static constexpr size_t kMipsStackOverflowReservedBytes = kDefaultStackOverflowReservedBytes;
-static constexpr size_t kMips64StackOverflowReservedBytes = kDefaultStackOverflowReservedBytes;
-
-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;
+#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 = 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 ac8f5233da..32768b0263 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 @@ MACRO0(RETURN_OR_DELIVER_PENDING_EXCEPTION)
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 @@ END_FUNCTION art_quick_alloc_object_rosalloc
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 @@ MACRO1(ALLOC_OBJECT_TLAB_FAST_PATH, slowPathLabel)
// 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 r8d
+ 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 @@ MACRO1(ALLOC_OBJECT_TLAB_SLOW_PATH, cxx_name)
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 @@ DEFINE_FUNCTION art_quick_alloc_object_tlab
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 @@ DEFINE_FUNCTION art_quick_alloc_object_region_tlab
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 @@ DEFINE_FUNCTION art_quick_alloc_object_region_tlab
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 6323eee53a..ac21a3f0ea 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -57,6 +57,7 @@ struct LogVerbosity {
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 3c43253e67..5a50247f5a 100644
--- a/runtime/base/macros.h
+++ b/runtime/base/macros.h
@@ -75,7 +75,7 @@ template<typename T> ART_FRIEND_TEST(test_set_name, individual_test)
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 @@ char (&ArraySizeHelper(T (&array)[N]))[N];
#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/class_linker.cc b/runtime/class_linker.cc
index 46722ecad7..4d48da6a83 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4630,18 +4630,23 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
} else {
value_it.ReadValueToField<false>(field);
}
+ if (self->IsExceptionPending()) {
+ break;
+ }
DCHECK(!value_it.HasNext() || field_it.HasNextStaticField());
}
}
}
- ArtMethod* clinit = klass->FindClassInitializer(image_pointer_size_);
- if (clinit != nullptr) {
- CHECK(can_init_statics);
- JValue result;
- clinit->Invoke(self, nullptr, 0, &result, "V");
- }
+ if (!self->IsExceptionPending()) {
+ ArtMethod* clinit = klass->FindClassInitializer(image_pointer_size_);
+ if (clinit != nullptr) {
+ CHECK(can_init_statics);
+ JValue result;
+ clinit->Invoke(self, nullptr, 0, &result, "V");
+ }
+ }
self->AllowThreadSuspension();
uint64_t t1 = NanoTime();
diff --git a/runtime/experimental_flags.h b/runtime/experimental_flags.h
index fde1a5f3ab..7faa2dc7e3 100644
--- a/runtime/experimental_flags.h
+++ b/runtime/experimental_flags.h
@@ -26,6 +26,8 @@ struct ExperimentalFlags {
// The actual flag values.
enum {
kNone = 0x0000,
+ kAgents = 0x0001, // 0b00000001
+ kRuntimePlugins = 0x0002, // 0b00000010
};
constexpr ExperimentalFlags() : value_(0x0000) {}
@@ -61,9 +63,19 @@ struct ExperimentalFlags {
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 88fbf781bc..b574c3bf3a 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -230,6 +230,9 @@ Heap::Heap(size_t initial_size,
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/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index c2e2a1edd2..6fcad295bb 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -384,30 +384,7 @@ ImageSpace* ImageSpace::CreateBootImage(const char* image_location,
&has_system, &cache_filename, &dalvik_cache_exists,
&has_cache, &is_global_cache);
- // If we're starting with the global cache, and we're the zygote, try to see whether there are
- // OTA artifacts from the A/B OTA preopting to move over.
- // (It is structurally simpler to check this here, instead of complicating the compile/relocate
- // logic below.)
const bool is_zygote = Runtime::Current()->IsZygote();
- if (is_global_cache && is_zygote) {
- VLOG(startup) << "Checking for A/B OTA data.";
- TryMoveOTAArtifacts(cache_filename, dalvik_cache_exists);
-
- // Retry. There are two cases where the old info is outdated:
- // * There wasn't a boot image before (e.g., some failure on boot), but now the OTA preopted
- // image has been moved in-place.
- // * There was a boot image before, and we tried to move the OTA preopted image, but a failure
- // happened and there is no file anymore.
- found_image = FindImageFilename(image_location,
- image_isa,
- &system_filename,
- &has_system,
- &cache_filename,
- &dalvik_cache_exists,
- &has_cache,
- &is_global_cache);
- }
-
if (is_zygote && !secondary_image) {
MarkZygoteStart(image_isa, Runtime::Current()->GetZygoteMaxFailedBoots());
}
@@ -529,6 +506,17 @@ ImageSpace* ImageSpace::CreateBootImage(const char* image_location,
error_msg);
}
if (space != nullptr) {
+ // Check whether there is enough space left over in the data partition. Even if we can load
+ // the image, we need to be conservative, as some parts of the platform are not very tolerant
+ // of space constraints.
+ // ImageSpace doesn't know about the data partition per se, it relies on the FindImageFilename
+ // helper (which relies on GetDalvikCache). So for now, if we load an image out of /system,
+ // ignore the check (as it would test for free space in /system instead).
+ if (!is_system && !CheckSpace(*image_filename, error_msg)) {
+ // No. Delete the generated image and try to run out of the dex files.
+ PruneDalvikCache(image_isa);
+ return nullptr;
+ }
return space;
}
diff --git a/runtime/gc/space/image_space_fs.h b/runtime/gc/space/image_space_fs.h
index 8e852fa54b..fa941c0376 100644
--- a/runtime/gc/space/image_space_fs.h
+++ b/runtime/gc/space/image_space_fs.h
@@ -79,115 +79,6 @@ static void DeleteDirectoryContents(const std::string& dir, bool recurse) {
CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory.";
}
-static bool HasContent(const char* dir) {
- if (!OS::DirectoryExists(dir)) {
- return false;
- }
- DIR* c_dir = opendir(dir);
- if (c_dir == nullptr) {
- PLOG(WARNING) << "Unable to open " << dir << " to delete it if empty";
- return false;
- }
-
- for (struct dirent* de = readdir(c_dir); de != nullptr; de = readdir(c_dir)) {
- const char* name = de->d_name;
- if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
- continue;
- }
- // Something here.
- CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory.";
- return true;
- }
- CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory.";
- return false;
-}
-
-// Delete this directory, if empty. Then repeat with the parents. Skips non-existing directories.
-// If stop_at isn't null, the recursion will stop when a directory with the given name is found.
-static void DeleteEmptyDirectoriesUpTo(const std::string& dir, const char* stop_at) {
- if (HasContent(dir.c_str())) {
- return;
- }
- if (stop_at != nullptr) {
- // This check isn't precise, but good enough in practice.
- if (EndsWith(dir, stop_at)) {
- return;
- }
- }
- if (OS::DirectoryExists(dir.c_str())) {
- if (rmdir(dir.c_str()) != 0) {
- PLOG(ERROR) << "Unable to rmdir " << dir;
- return;
- }
- }
- size_t last_slash = dir.rfind('/');
- if (last_slash != std::string::npos) {
- DeleteEmptyDirectoriesUpTo(dir.substr(0, last_slash), stop_at);
- }
-}
-
-static void MoveOTAArtifacts(const char* src, const char* trg) {
- DCHECK(OS::DirectoryExists(src));
- DCHECK(OS::DirectoryExists(trg));
-
- if (HasContent(trg)) {
- LOG(WARNING) << "We do not support merging caches, but the target isn't empty: " << src
- << " to " << trg;
- return;
- }
-
- if (rename(src, trg) != 0) {
- PLOG(ERROR) << "Could not rename OTA cache " << src << " to target " << trg;
- }
-}
-
-// This is some dlopen/dlsym and hardcoded data to avoid a dependency on libselinux. Make sure
-// this stays in sync!
-static bool RelabelOTAFiles(const std::string& dalvik_cache_dir) {
- // We only expect selinux on devices. Don't even attempt this on the host.
- if (!kIsTargetBuild) {
- return true;
- }
-
- // Custom deleter, so we can use std::unique_ptr.
- struct HandleDeleter {
- void operator()(void* in) {
- if (in != nullptr && dlclose(in) != 0) {
- PLOG(ERROR) << "Could not close selinux handle.";
- }
- }
- };
-
- // Look for selinux library.
- std::unique_ptr<void, HandleDeleter> selinux_handle(dlopen("libselinux.so", RTLD_NOW));
- if (selinux_handle == nullptr) {
- // Assume everything's OK if we can't open the library.
- return true;
- }
- dlerror(); // Clean dlerror string.
-
- void* restorecon_ptr = dlsym(selinux_handle.get(), "selinux_android_restorecon");
- if (restorecon_ptr == nullptr) {
- // Can't find the relabel function. That's bad. Make sure the zygote fails, as we have no
- // other recourse to make this error obvious.
- const char* error_string = dlerror();
- LOG(FATAL) << "Could not find selinux restorecon function: "
- << ((error_string != nullptr) ? error_string : "(unknown error)");
- UNREACHABLE();
- }
-
- using RestoreconFn = int (*)(const char*, unsigned int);
- constexpr unsigned int kRecursive = 4U;
-
- RestoreconFn restorecon_fn = reinterpret_cast<RestoreconFn>(restorecon_ptr);
- if (restorecon_fn(dalvik_cache_dir.c_str(), kRecursive) != 0) {
- LOG(ERROR) << "Failed to restorecon " << dalvik_cache_dir;
- return false;
- }
-
- return true;
-}
-
} // namespace impl
@@ -226,8 +117,21 @@ static void MarkZygoteStart(const InstructionSet isa, const uint32_t max_failed_
file.reset(OS::CreateEmptyFile(file_name));
if (file.get() == nullptr) {
+ int saved_errno = errno;
PLOG(WARNING) << "Failed to create boot marker.";
- return;
+ if (saved_errno != ENOSPC) {
+ return;
+ }
+
+ LOG(WARNING) << "Pruning dalvik cache because of low-memory situation.";
+ impl::DeleteDirectoryContents(isa_subdir, false);
+
+ // Try once more.
+ file.reset(OS::OpenFileReadWrite(file_name));
+ if (file == nullptr) {
+ PLOG(WARNING) << "Failed to create boot marker.";
+ return;
+ }
}
} else {
if (!file->ReadFully(&num_failed_boots, sizeof(num_failed_boots))) {
@@ -262,53 +166,6 @@ static void MarkZygoteStart(const InstructionSet isa, const uint32_t max_failed_
}
}
-static void TryMoveOTAArtifacts(const std::string& cache_filename, bool dalvik_cache_exists) {
- // We really assume here global means /data/dalvik-cache, and we'll inject 'ota.' Make sure
- // that's true.
- CHECK(StartsWith(cache_filename, "/data/dalvik-cache")) << cache_filename;
-
- // Inject ota subdirectory.
- std::string ota_filename(cache_filename);
- ota_filename = ota_filename.insert(strlen("/data/"), "ota/");
- CHECK(StartsWith(ota_filename, "/data/ota/dalvik-cache")) << ota_filename;
-
- // See if the file exists.
- if (OS::FileExists(ota_filename.c_str())) {
- VLOG(startup) << "OTA directory does exist, checking for artifacts";
-
- size_t last_slash = ota_filename.rfind('/');
- CHECK_NE(last_slash, std::string::npos);
- std::string ota_source_dir = ota_filename.substr(0, last_slash);
-
- // We need the dalvik cache now, really.
- if (dalvik_cache_exists) {
- size_t last_cache_slash = cache_filename.rfind('/');
- DCHECK_NE(last_cache_slash, std::string::npos);
- std::string dalvik_cache_target_dir = cache_filename.substr(0, last_cache_slash);
-
- // First clean the target cache.
- impl::DeleteDirectoryContents(dalvik_cache_target_dir.c_str(), false);
-
- // Now move things over.
- impl::MoveOTAArtifacts(ota_source_dir.c_str(), dalvik_cache_target_dir.c_str());
-
- // Last step: ensure the files have the right selinux label.
- if (!impl::RelabelOTAFiles(dalvik_cache_target_dir)) {
- // This isn't good. We potentially moved files, but they have the wrong label. Delete the
- // files.
- LOG(WARNING) << "Could not relabel files, must delete dalvik-cache.";
- impl::DeleteDirectoryContents(dalvik_cache_target_dir.c_str(), false);
- }
- }
-
- // Cleanup.
- impl::DeleteDirectoryContents(ota_source_dir.c_str(), true);
- impl::DeleteEmptyDirectoriesUpTo(ota_source_dir, "ota");
- } else {
- VLOG(startup) << "No OTA directory.";
- }
-}
-
} // namespace space
} // namespace gc
} // namespace art
diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h
index 96924722d8..716c23d1b0 100644
--- a/runtime/generated/asm_support_gen.h
+++ b/runtime/generated/asm_support_gen.h
@@ -98,6 +98,8 @@ DEFINE_CHECK_EQ(static_cast<uint32_t>(LOCK_WORD_MARK_BIT_MASK_SHIFTED), (static_
DEFINE_CHECK_EQ(static_cast<size_t>(OBJECT_ALIGNMENT_MASK), (static_cast<size_t>(art::kObjectAlignment - 1)))
#define OBJECT_ALIGNMENT_MASK_TOGGLED 0xfffffff8
DEFINE_CHECK_EQ(static_cast<uint32_t>(OBJECT_ALIGNMENT_MASK_TOGGLED), (static_cast<uint32_t>(~static_cast<uint32_t>(art::kObjectAlignment - 1))))
+#define OBJECT_ALIGNMENT_MASK_TOGGLED64 0xfffffffffffffff8
+DEFINE_CHECK_EQ(static_cast<uint64_t>(OBJECT_ALIGNMENT_MASK_TOGGLED64), (static_cast<uint64_t>(~static_cast<uint64_t>(art::kObjectAlignment - 1))))
#define ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE 128
DEFINE_CHECK_EQ(static_cast<int32_t>(ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE), (static_cast<int32_t>((art::gc::allocator::RosAlloc::kMaxThreadLocalBracketSize))))
#define ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT 3
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index c644cde5db..2401bec9f3 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -48,7 +48,7 @@ static size_t gGlobalsMax = 51200; // Arbitrary sanity check. (Must fit in 16 b
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 @@ class JII {
}
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 @@ class JII {
*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 @@ class JII {
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 @@ JavaVMExt::JavaVMExt(Runtime* runtime, const RuntimeArgumentMap& runtime_options
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(Runtime* runtime, const RuntimeArgumentMap& runtime_options
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 @@ bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
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 @@ void JavaVMExt::VisitRoots(RootVisitor* visitor) {
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 3d055cd7ce..ed9d3abfe2 100644
--- a/runtime/java_vm_ext.h
+++ b/runtime/java_vm_ext.h
@@ -36,6 +36,10 @@ class ParsedOptions;
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 @@ class JavaVMExt : public JavaVM {
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 @@ class JavaVMExt : public JavaVM {
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/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 5a469e51b4..b35c958b0b 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -176,14 +176,13 @@ void ProfileSaver::NotifyJitActivityInternal() {
MutexLock wait_mutex(Thread::Current(), wait_lock_);
if ((NanoTime() - last_time_ns_saver_woke_up_) > MsToNs(options_.GetMinSavePeriodMs())) {
WakeUpSaver();
+ } else if (jit_activity_notifications_ > options_.GetMaxNotificationBeforeWake()) {
+ // Make sure to wake up the saver if we see a spike in the number of notifications.
+ // This is a precaution to avoid losing a big number of methods in case
+ // this is a spike with no jit after.
+ total_number_of_hot_spikes_++;
+ WakeUpSaver();
}
- } else if (jit_activity_notifications_ > options_.GetMaxNotificationBeforeWake()) {
- // Make sure to wake up the saver if we see a spike in the number of notifications.
- // This is a precaution to avoid "loosing" a big number of methods in case
- // this is a spike with no jit after.
- total_number_of_hot_spikes_++;
- MutexLock wait_mutex(Thread::Current(), wait_lock_);
- WakeUpSaver();
}
}
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
index 1ee1611ef7..40efc898b8 100644
--- a/runtime/jni_env_ext.cc
+++ b/runtime/jni_env_ext.cc
@@ -45,6 +45,20 @@ static bool CheckLocalsValid(JNIEnvExt* in) NO_THREAD_SAFETY_ANALYSIS {
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 d4accc342b..ac287d488a 100644
--- a/runtime/jni_env_ext.h
+++ b/runtime/jni_env_ext.h
@@ -54,6 +54,8 @@ struct JNIEnvExt : public JNIEnv {
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 c7e4f8b343..174da79030 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 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize
.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 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize
.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 @@ bool ParsedOptions::DoParse(const RuntimeOptions& options,
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 @@ void ParsedOptions::Usage(const char* fmt, ...) {
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 @@ void ParsedOptions::Usage(const char* fmt, ...) {
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 0000000000..481b1caa15
--- /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 0000000000..18f3977bd5
--- /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 68fa0d32be..ddcfb6d5aa 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 @@ Runtime::~Runtime() {
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 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
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 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
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 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
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 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
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 c971646195..6da60f27a3 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -63,6 +63,9 @@ namespace mirror {
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 MonitorList;
class MonitorPool;
class NullPointerHandler;
class OatFileManager;
+class Plugin;
struct RuntimeArgumentMap;
class SignalCatcher;
class StackOverflowHandler;
@@ -698,6 +702,9 @@ class Runtime {
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 b95dfad550..146afc7ad8 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -117,7 +117,10 @@ RUNTIME_OPTIONS_KEY (unsigned int, ZygoteMaxFailedBoots, 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/simulator/Android.mk b/runtime/simulator/Android.mk
index 953a37733d..a34a84100a 100644
--- a/runtime/simulator/Android.mk
+++ b/runtime/simulator/Android.mk
@@ -88,9 +88,9 @@ define build-libart-simulator
LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
# For simulator_arm64.
ifeq ($$(art_ndebug_or_debug),debug)
- LOCAL_SHARED_LIBRARIES += libvixl-arm64
+ LOCAL_SHARED_LIBRARIES += libvixld-arm64
else
- LOCAL_SHARED_LIBRARIES += libvixl-arm64
+ LOCAL_SHARED_LIBRARIES += libvixl-arm64
endif
ifeq ($$(art_target_or_host),target)
include $(BUILD_SHARED_LIBRARY)
diff --git a/runtime/simulator/code_simulator_arm64.h b/runtime/simulator/code_simulator_arm64.h
index 69388b122c..59ea34fb80 100644
--- a/runtime/simulator/code_simulator_arm64.h
+++ b/runtime/simulator/code_simulator_arm64.h
@@ -20,10 +20,10 @@
#include "memory"
#include "simulator/code_simulator.h"
-// TODO: make vixl clean wrt -Wshadow.
+// TODO(VIXL): Make VIXL compile with -Wshadow.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
-#include "a64/simulator-a64.h"
+#include "aarch64/simulator-aarch64.h"
#pragma GCC diagnostic pop
namespace art {
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 4647d67699..dd7e53100f 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -1050,7 +1050,7 @@ struct CodeInfoEncoding {
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 0000000000..41a21f70f3
--- /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 0000000000..521e21e4e4
--- /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_
+