Merge "ART: Remove binary dependencies in 952-invoke-custom-lookup"
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 17b94d3..ac5c6fb 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -77,7 +77,9 @@
std::vector<uint8_t> elf_file = debug::WriteDebugElfFileForClasses(
kRuntimeISA, jit_compiler->GetCompilerDriver()->GetInstructionSetFeatures(), types_array);
MutexLock mu(Thread::Current(), *Locks::native_debug_interface_lock_);
- CreateJITCodeEntry(std::move(elf_file));
+ // We never free debug info for types, so we don't need to provide a handle
+ // (which would have been otherwise used as identifier to remove it later).
+ AddNativeDebugInfoForJit(nullptr /* handle */, elf_file);
}
}
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index b3f23a0..e42dfc1 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -1411,13 +1411,12 @@
mini_debug_info,
ArrayRef<const debug::MethodDebugInfo>(&info, 1));
MutexLock mu(Thread::Current(), *Locks::native_debug_interface_lock_);
- JITCodeEntry* entry = CreateJITCodeEntry(elf_file);
- IncrementJITCodeEntryRefcount(entry, info.code_address);
+ AddNativeDebugInfoForJit(reinterpret_cast<const void*>(info.code_address), elf_file);
VLOG(jit)
<< "JIT mini-debug-info added for " << ArtMethod::PrettyMethod(method)
<< " size=" << PrettySize(elf_file.size())
- << " total_size=" << PrettySize(GetJITCodeEntryMemUsage());
+ << " total_size=" << PrettySize(GetJitNativeDebugInfoMemUsage());
}
} // namespace art
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 27025d1..46e0ee4 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -18,7 +18,10 @@
// we use gold as the linker (arm, x86, x86_64). The symbol is used by the debuggers to detect when
// new jit code is generated. We don't want it to be called when a different function with the same
// (empty) body is called.
-JIT_DEBUG_REGISTER_CODE_LDFLAGS = ["-Wl,--keep-unique,__jit_debug_register_code"]
+JIT_DEBUG_REGISTER_CODE_LDFLAGS = [
+ "-Wl,--keep-unique,__jit_debug_register_code",
+ "-Wl,--keep-unique,__dex_debug_register_code"
+]
cc_defaults {
name: "libart_defaults",
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index a4c32dd..8a24daa 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -1218,6 +1218,10 @@
DCHECK(jni_function_table_lock_ == nullptr);
jni_function_table_lock_ = new Mutex("JNI function table lock", current_lock_level);
+ UPDATE_CURRENT_LOCK_LEVEL(kNativeDebugInterfaceLock);
+ DCHECK(native_debug_interface_lock_ == nullptr);
+ native_debug_interface_lock_ = new Mutex("Native debug interface lock", current_lock_level);
+
UPDATE_CURRENT_LOCK_LEVEL(kAbortLock);
DCHECK(abort_lock_ == nullptr);
abort_lock_ = new Mutex("abort lock", current_lock_level, true);
@@ -1230,10 +1234,6 @@
DCHECK(unexpected_signal_lock_ == nullptr);
unexpected_signal_lock_ = new Mutex("unexpected signal lock", current_lock_level, true);
- UPDATE_CURRENT_LOCK_LEVEL(kNativeDebugInterfaceLock);
- DCHECK(native_debug_interface_lock_ == nullptr);
- native_debug_interface_lock_ = new Mutex("Native debug interface lock", current_lock_level);
-
UPDATE_CURRENT_LOCK_LEVEL(kLoggingLock);
DCHECK(logging_lock_ == nullptr);
logging_lock_ = new Mutex("logging lock", current_lock_level, true);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index bf27b7f..4f7001a 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -58,11 +58,11 @@
// [1] http://www.drdobbs.com/parallel/use-lock-hierarchies-to-avoid-deadlock/204801163
enum LockLevel {
kLoggingLock = 0,
- kNativeDebugInterfaceLock,
kSwapMutexesLock,
kUnexpectedSignalLock,
kThreadSuspendCountLock,
kAbortLock,
+ kNativeDebugInterfaceLock,
kSignalHandlingLock,
kJdwpAdbStateLock,
kJdwpSocketLock,
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index ad2e7a7..856e538 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3434,7 +3434,8 @@
data.weak_root = dex_cache_jweak;
data.dex_file = dex_cache->GetDexFile();
data.class_table = ClassTableForClassLoader(class_loader);
- RegisterDexFileForNative(self, data.dex_file->Begin());
+ AddNativeDebugInfoForDex(self, ArrayRef<const uint8_t>(data.dex_file->Begin(),
+ data.dex_file->Size()));
DCHECK(data.class_table != nullptr);
// Make sure to hold the dex cache live in the class table. This case happens for the boot class
// path dex caches without an image.
diff --git a/runtime/elf_file_impl.h b/runtime/elf_file_impl.h
index 04c2243..3143df5 100644
--- a/runtime/elf_file_impl.h
+++ b/runtime/elf_file_impl.h
@@ -28,10 +28,6 @@
namespace art {
-extern "C" {
- struct JITCodeEntry;
-}
-
template <typename ElfTypes>
class ElfFileImpl {
public:
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 6f9cad8..d8f858e 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -83,10 +83,14 @@
#define BRANCH_INSTRUMENTATION(offset) \
do { \
if (UNLIKELY(instrumentation->HasBranchListeners())) { \
- instrumentation->Branch(self, method, dex_pc, offset); \
+ instrumentation->Branch(self, shadow_frame.GetMethod(), dex_pc, offset); \
} \
JValue result; \
- if (jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, &result)) { \
+ if (jit::Jit::MaybeDoOnStackReplacement(self, \
+ shadow_frame.GetMethod(), \
+ dex_pc, \
+ offset, \
+ &result)) { \
if (interpret_one_instruction) { \
/* OSR has completed execution of the method. Signal mterp to return to caller */ \
shadow_frame.SetDexPC(dex::kDexNoIndex); \
@@ -98,7 +102,7 @@
#define HOTNESS_UPDATE() \
do { \
if (jit != nullptr) { \
- jit->AddSamples(self, method, 1, /*with_backedges*/ true); \
+ jit->AddSamples(self, shadow_frame.GetMethod(), 1, /*with_backedges*/ true); \
} \
} while (false)
@@ -200,7 +204,6 @@
uint32_t dex_pc = shadow_frame.GetDexPC();
const auto* const instrumentation = Runtime::Current()->GetInstrumentation();
- ArtMethod* method = shadow_frame.GetMethod();
const uint16_t* const insns = accessor.Insns();
const Instruction* inst = Instruction::At(insns + dex_pc);
uint16_t inst_data;
@@ -390,7 +393,7 @@
const size_t ref_idx = inst->VRegA_11x(inst_data);
ObjPtr<mirror::Object> obj_result = shadow_frame.GetVRegReference(ref_idx);
if (do_assignability_check && obj_result != nullptr) {
- ObjPtr<mirror::Class> return_type = method->ResolveReturnType();
+ ObjPtr<mirror::Class> return_type = shadow_frame.GetMethod()->ResolveReturnType();
// Re-load since it might have moved.
obj_result = shadow_frame.GetVRegReference(ref_idx);
if (return_type == nullptr) {
@@ -535,7 +538,9 @@
case Instruction::CONST_METHOD_HANDLE: {
PREAMBLE();
ClassLinker* cl = Runtime::Current()->GetClassLinker();
- ObjPtr<mirror::MethodHandle> mh = cl->ResolveMethodHandle(self, inst->VRegB_21c(), method);
+ ObjPtr<mirror::MethodHandle> mh = cl->ResolveMethodHandle(self,
+ inst->VRegB_21c(),
+ shadow_frame.GetMethod());
if (UNLIKELY(mh == nullptr)) {
HANDLE_PENDING_EXCEPTION();
} else {
@@ -547,7 +552,9 @@
case Instruction::CONST_METHOD_TYPE: {
PREAMBLE();
ClassLinker* cl = Runtime::Current()->GetClassLinker();
- ObjPtr<mirror::MethodType> mt = cl->ResolveMethodType(self, inst->VRegB_21c(), method);
+ ObjPtr<mirror::MethodType> mt = cl->ResolveMethodType(self,
+ inst->VRegB_21c(),
+ shadow_frame.GetMethod());
if (UNLIKELY(mt == nullptr)) {
HANDLE_PENDING_EXCEPTION();
} else {
diff --git a/runtime/jit/debugger_interface.cc b/runtime/jit/debugger_interface.cc
index d60f70a..505e626 100644
--- a/runtime/jit/debugger_interface.cc
+++ b/runtime/jit/debugger_interface.cc
@@ -18,18 +18,35 @@
#include <android-base/logging.h>
+#include "base/array_ref.h"
#include "base/mutex.h"
+#include "base/time_utils.h"
#include "thread-current-inl.h"
#include "thread.h"
#include <unordered_map>
-namespace art {
+//
+// Debug interface for native tools (gdb, lldb, libunwind, simpleperf).
+//
+// See http://sourceware.org/gdb/onlinedocs/gdb/Declarations.html
+//
+// There are two ways for native tools to access the debug data safely:
+//
+// 1) Synchronously, by setting a breakpoint in the __*_debug_register_code
+// method, which is called after every modification of the linked list.
+// GDB does this, but it is complex to set up and it stops the process.
+//
+// 2) Asynchronously, by monitoring the action_counter_, which is incremented
+// on every modification of the linked list and kept at -1 during updates.
+// Therefore, if the tool process reads the counter both before and after
+// iterating over the linked list, and the counters match and are not -1,
+// the tool process can be sure the list was not modified during the read.
+// Obviously, it can also cache the data and use the counter to determine
+// if the cache is up to date, or to intelligently update it if needed.
+//
-// -------------------------------------------------------------------
-// Binary GDB JIT Interface as described in
-// http://sourceware.org/gdb/onlinedocs/gdb/Declarations.html
-// -------------------------------------------------------------------
+namespace art {
extern "C" {
typedef enum {
JIT_NOACTION = 0,
@@ -40,168 +57,193 @@
struct JITCodeEntry {
JITCodeEntry* next_;
JITCodeEntry* prev_;
- const uint8_t *symfile_addr_;
- uint64_t symfile_size_;
- uint32_t ref_count; // ART internal field.
+ const uint8_t* symfile_addr_;
+ uint64_t symfile_size_; // Beware of the offset (12 on x86; but 16 on ARM32).
+
+ // Android-specific fields:
+ uint64_t register_timestamp_; // CLOCK_MONOTONIC time of entry registration.
};
struct JITDescriptor {
- uint32_t version_;
- uint32_t action_flag_;
- JITCodeEntry* relevant_entry_;
- JITCodeEntry* first_entry_;
+ uint32_t version_ = 1; // NB: GDB supports only version 1.
+ uint32_t action_flag_ = JIT_NOACTION; // One of the JITAction enum values.
+ JITCodeEntry* relevant_entry_ = nullptr; // The entry affected by the action.
+ JITCodeEntry* first_entry_ = nullptr; // Head of link list of all entries.
+
+ // Android-specific fields:
+ uint8_t magic_[8] = {'A', 'n', 'd', 'r', 'o', 'i', 'd', '1'};
+ uint32_t flags_ = 0; // Reserved for future use. Must be 0.
+ uint32_t sizeof_descriptor = sizeof(JITDescriptor);
+ uint32_t sizeof_entry = sizeof(JITCodeEntry);
+ std::atomic_int32_t action_counter_; // Number of actions, or -1 if locked.
+ // It can overflow from INT32_MAX to 0.
+ uint64_t action_timestamp_ = 1; // CLOCK_MONOTONIC time of last action.
};
- // GDB will place breakpoint into this function.
- // To prevent GCC from inlining or removing it we place noinline attribute
- // and inline assembler statement inside.
- void __attribute__((noinline)) __jit_debug_register_code();
+ // Check that std::atomic_int32_t has the same layout as int32_t.
+ static_assert(alignof(std::atomic_int32_t) == alignof(int32_t), "Weird alignment");
+ static_assert(sizeof(std::atomic_int32_t) == sizeof(int32_t), "Weird size");
+
+ // GDB may set breakpoint here. We must ensure it is not removed or deduplicated.
void __attribute__((noinline)) __jit_debug_register_code() {
__asm__("");
}
- // Call __jit_debug_register_code indirectly via global variable.
- // This gives the debugger an easy way to inject custom code to handle the events.
+ // Alternatively, native tools may overwrite this field to execute custom handler.
void (*__jit_debug_register_code_ptr)() = __jit_debug_register_code;
- // GDB will inspect contents of this descriptor.
- // Static initialization is necessary to prevent GDB from seeing
- // uninitialized descriptor.
- JITDescriptor __jit_debug_descriptor = { 1, JIT_NOACTION, nullptr, nullptr };
+ // The root data structure describing of all JITed methods.
+ JITDescriptor __jit_debug_descriptor {};
- // Incremented whenever __jit_debug_descriptor is modified.
- uint32_t __jit_debug_descriptor_timestamp = 0;
-
- struct DEXFileEntry {
- DEXFileEntry* next_;
- DEXFileEntry* prev_;
- const void* dexfile_;
- };
-
- DEXFileEntry* __art_debug_dexfiles = nullptr;
-
- // Incremented whenever __art_debug_dexfiles is modified.
- uint32_t __art_debug_dexfiles_timestamp = 0;
-}
-
-static size_t g_jit_debug_mem_usage
- GUARDED_BY(Locks::native_debug_interface_lock_) = 0;
-
-static std::unordered_map<const void*, DEXFileEntry*> g_dexfile_entries
- GUARDED_BY(Locks::native_debug_interface_lock_);
-
-void RegisterDexFileForNative(Thread* current_thread, const void* dexfile_header) {
- MutexLock mu(current_thread, *Locks::native_debug_interface_lock_);
- if (g_dexfile_entries.count(dexfile_header) == 0) {
- DEXFileEntry* entry = new DEXFileEntry();
- CHECK(entry != nullptr);
- entry->dexfile_ = dexfile_header;
- entry->prev_ = nullptr;
- entry->next_ = __art_debug_dexfiles;
- if (entry->next_ != nullptr) {
- entry->next_->prev_ = entry;
- }
- __art_debug_dexfiles = entry;
- __art_debug_dexfiles_timestamp++;
- g_dexfile_entries.emplace(dexfile_header, entry);
+ // The following globals mirror the ones above, but are used to register dex files.
+ void __attribute__((noinline)) __dex_debug_register_code() {
+ __asm__("");
}
+ void (*__dex_debug_register_code_ptr)() = __dex_debug_register_code;
+ JITDescriptor __dex_debug_descriptor {};
}
-void DeregisterDexFileForNative(Thread* current_thread, const void* dexfile_header) {
- MutexLock mu(current_thread, *Locks::native_debug_interface_lock_);
- auto it = g_dexfile_entries.find(dexfile_header);
- // We register dex files in the class linker and free them in DexFile_closeDexFile,
- // but might be cases where we load the dex file without using it in the class linker.
- if (it != g_dexfile_entries.end()) {
- DEXFileEntry* entry = it->second;
- if (entry->prev_ != nullptr) {
- entry->prev_->next_ = entry->next_;
- } else {
- __art_debug_dexfiles = entry->next_;
- }
- if (entry->next_ != nullptr) {
- entry->next_->prev_ = entry->prev_;
- }
- __art_debug_dexfiles_timestamp++;
- delete entry;
- g_dexfile_entries.erase(it);
- }
+// Mark the descriptor as "locked", so native tools know the data is unstable.
+// Returns the old value of the counter.
+static int32_t LockActionCounter(JITDescriptor& descriptor) {
+ return descriptor.action_counter_.exchange(-1);
}
-JITCodeEntry* CreateJITCodeEntry(const std::vector<uint8_t>& symfile) {
- DCHECK_NE(symfile.size(), 0u);
+// Mark the descriptor as "unlocked", so native tools know the data is safe to read.
+// It will also increment the value so that the tools know the data has changed.
+static void UnlockActionCounter(JITDescriptor& descriptor, int32_t old_value) {
+ int32_t new_value = (old_value + 1) & 0x7FFFFFFF; // Handle overflow to avoid -1.
+ descriptor.action_counter_.store(new_value);
+}
- // Make a copy of the buffer. We want to shrink it anyway.
- uint8_t* symfile_copy = new uint8_t[symfile.size()];
- CHECK(symfile_copy != nullptr);
- memcpy(symfile_copy, symfile.data(), symfile.size());
+static JITCodeEntry* CreateJITCodeEntryInternal(
+ JITDescriptor& descriptor,
+ void (*register_code_ptr)(),
+ const ArrayRef<const uint8_t>& symfile)
+ REQUIRES(Locks::native_debug_interface_lock_) {
+ int32_t old_action_counter = LockActionCounter(descriptor);
JITCodeEntry* entry = new JITCodeEntry;
CHECK(entry != nullptr);
- entry->symfile_addr_ = symfile_copy;
+ entry->symfile_addr_ = symfile.data();
entry->symfile_size_ = symfile.size();
entry->prev_ = nullptr;
- entry->ref_count = 0;
- entry->next_ = __jit_debug_descriptor.first_entry_;
+ entry->next_ = descriptor.first_entry_;
+ entry->register_timestamp_ = NanoTime();
if (entry->next_ != nullptr) {
entry->next_->prev_ = entry;
}
- g_jit_debug_mem_usage += sizeof(JITCodeEntry) + entry->symfile_size_;
- __jit_debug_descriptor.first_entry_ = entry;
- __jit_debug_descriptor.relevant_entry_ = entry;
- __jit_debug_descriptor.action_flag_ = JIT_REGISTER_FN;
- __jit_debug_descriptor_timestamp++;
- (*__jit_debug_register_code_ptr)();
+ descriptor.first_entry_ = entry;
+ descriptor.relevant_entry_ = entry;
+ descriptor.action_flag_ = JIT_REGISTER_FN;
+ descriptor.action_timestamp_ = entry->register_timestamp_;
+ UnlockActionCounter(descriptor, old_action_counter);
+
+ (*register_code_ptr)();
return entry;
}
-void DeleteJITCodeEntry(JITCodeEntry* entry) {
+static void DeleteJITCodeEntryInternal(
+ JITDescriptor& descriptor,
+ void (*register_code_ptr)(),
+ JITCodeEntry* entry)
+ REQUIRES(Locks::native_debug_interface_lock_) {
+ CHECK(entry != nullptr);
+ int32_t old_action_counter = LockActionCounter(descriptor);
+
if (entry->prev_ != nullptr) {
entry->prev_->next_ = entry->next_;
} else {
- __jit_debug_descriptor.first_entry_ = entry->next_;
+ descriptor.first_entry_ = entry->next_;
}
-
if (entry->next_ != nullptr) {
entry->next_->prev_ = entry->prev_;
}
- g_jit_debug_mem_usage -= sizeof(JITCodeEntry) + entry->symfile_size_;
- __jit_debug_descriptor.relevant_entry_ = entry;
- __jit_debug_descriptor.action_flag_ = JIT_UNREGISTER_FN;
- __jit_debug_descriptor_timestamp++;
- (*__jit_debug_register_code_ptr)();
- delete[] entry->symfile_addr_;
+ descriptor.relevant_entry_ = entry;
+ descriptor.action_flag_ = JIT_UNREGISTER_FN;
+ descriptor.action_timestamp_ = NanoTime();
+ UnlockActionCounter(descriptor, old_action_counter);
+
+ (*register_code_ptr)();
delete entry;
}
-// Mapping from code address to entry. Used to manage life-time of the entries.
-static std::unordered_map<uintptr_t, JITCodeEntry*> g_jit_code_entries
+static std::unordered_map<const void*, JITCodeEntry*> __dex_debug_entries
GUARDED_BY(Locks::native_debug_interface_lock_);
-void IncrementJITCodeEntryRefcount(JITCodeEntry* entry, uintptr_t code_address) {
- DCHECK(entry != nullptr);
- DCHECK_EQ(g_jit_code_entries.count(code_address), 0u);
- entry->ref_count++;
- g_jit_code_entries.emplace(code_address, entry);
-}
-
-void DecrementJITCodeEntryRefcount(JITCodeEntry* entry, uintptr_t code_address) {
- DCHECK(entry != nullptr);
- DCHECK(g_jit_code_entries[code_address] == entry);
- if (--entry->ref_count == 0) {
- DeleteJITCodeEntry(entry);
+void AddNativeDebugInfoForDex(Thread* current_thread, ArrayRef<const uint8_t> dexfile) {
+ MutexLock mu(current_thread, *Locks::native_debug_interface_lock_);
+ DCHECK(dexfile.data() != nullptr);
+ // This is just defensive check. The class linker should not register the dex file twice.
+ if (__dex_debug_entries.count(dexfile.data()) == 0) {
+ JITCodeEntry* entry = CreateJITCodeEntryInternal(__dex_debug_descriptor,
+ __dex_debug_register_code_ptr,
+ dexfile);
+ __dex_debug_entries.emplace(dexfile.data(), entry);
}
- g_jit_code_entries.erase(code_address);
}
-JITCodeEntry* GetJITCodeEntry(uintptr_t code_address) {
- auto it = g_jit_code_entries.find(code_address);
- return it == g_jit_code_entries.end() ? nullptr : it->second;
+void RemoveNativeDebugInfoForDex(Thread* current_thread, ArrayRef<const uint8_t> dexfile) {
+ MutexLock mu(current_thread, *Locks::native_debug_interface_lock_);
+ auto it = __dex_debug_entries.find(dexfile.data());
+ // We register dex files in the class linker and free them in DexFile_closeDexFile, but
+ // there might be cases where we load the dex file without using it in the class linker.
+ if (it != __dex_debug_entries.end()) {
+ DeleteJITCodeEntryInternal(__dex_debug_descriptor,
+ __dex_debug_register_code_ptr,
+ it->second);
+ __dex_debug_entries.erase(it);
+ }
}
-size_t GetJITCodeEntryMemUsage() {
- return g_jit_debug_mem_usage + g_jit_code_entries.size() * 2 * sizeof(void*);
+static size_t __jit_debug_mem_usage
+ GUARDED_BY(Locks::native_debug_interface_lock_) = 0;
+
+// Mapping from handle to entry. Used to manage life-time of the entries.
+static std::unordered_map<const void*, JITCodeEntry*> __jit_debug_entries
+ GUARDED_BY(Locks::native_debug_interface_lock_);
+
+void AddNativeDebugInfoForJit(const void* handle, const std::vector<uint8_t>& symfile) {
+ DCHECK_NE(symfile.size(), 0u);
+
+ // Make a copy of the buffer to shrink it and to pass ownership to JITCodeEntry.
+ uint8_t* copy = new uint8_t[symfile.size()];
+ CHECK(copy != nullptr);
+ memcpy(copy, symfile.data(), symfile.size());
+
+ JITCodeEntry* entry = CreateJITCodeEntryInternal(
+ __jit_debug_descriptor,
+ __jit_debug_register_code_ptr,
+ ArrayRef<const uint8_t>(copy, symfile.size()));
+ __jit_debug_mem_usage += sizeof(JITCodeEntry) + entry->symfile_size_;
+
+ // We don't provide handle for type debug info, which means we cannot free it later.
+ // (this only happens when --generate-debug-info flag is enabled for the purpose
+ // of being debugged with gdb; it does not happen for debuggable apps by default).
+ bool ok = handle == nullptr || __jit_debug_entries.emplace(handle, entry).second;
+ DCHECK(ok) << "Native debug entry already exists for " << std::hex << handle;
+}
+
+void RemoveNativeDebugInfoForJit(const void* handle) {
+ auto it = __jit_debug_entries.find(handle);
+ // We generate JIT native debug info only if the right runtime flags are enabled,
+ // but we try to remove it unconditionally whenever code is freed from JIT cache.
+ if (it != __jit_debug_entries.end()) {
+ JITCodeEntry* entry = it->second;
+ const uint8_t* symfile_addr = entry->symfile_addr_;
+ uint64_t symfile_size = entry->symfile_size_;
+ DeleteJITCodeEntryInternal(__jit_debug_descriptor,
+ __jit_debug_register_code_ptr,
+ entry);
+ __jit_debug_entries.erase(it);
+ __jit_debug_mem_usage -= sizeof(JITCodeEntry) + symfile_size;
+ delete[] symfile_addr;
+ }
+}
+
+size_t GetJitNativeDebugInfoMemUsage() {
+ return __jit_debug_mem_usage + __jit_debug_entries.size() * 2 * sizeof(void*);
}
} // namespace art
diff --git a/runtime/jit/debugger_interface.h b/runtime/jit/debugger_interface.h
index 8c4bb3f..3d25910 100644
--- a/runtime/jit/debugger_interface.h
+++ b/runtime/jit/debugger_interface.h
@@ -26,45 +26,26 @@
namespace art {
-extern "C" {
- struct JITCodeEntry;
-}
-
// Notify native tools (e.g. libunwind) that DEX file has been opened.
-// The pointer needs to point the start of the dex data (not the DexFile* object).
-void RegisterDexFileForNative(Thread* current_thread, const void* dexfile_header);
+// It takes the lock itself. The parameter must point to dex data (not the DexFile* object).
+void AddNativeDebugInfoForDex(Thread* current_thread, ArrayRef<const uint8_t> dexfile);
// Notify native tools (e.g. libunwind) that DEX file has been closed.
-// The pointer needs to point the start of the dex data (not the DexFile* object).
-void DeregisterDexFileForNative(Thread* current_thread, const void* dexfile_header);
+// It takes the lock itself. The parameter must point to dex data (not the DexFile* object).
+void RemoveNativeDebugInfoForDex(Thread* current_thread, ArrayRef<const uint8_t> dexfile);
-// Notify native debugger about new JITed code by passing in-memory ELF.
-// It takes ownership of the in-memory ELF file.
-JITCodeEntry* CreateJITCodeEntry(const std::vector<uint8_t>& symfile)
+// Notify native tools about new JITed code by passing in-memory ELF.
+// The handle is the object that is being described (needed to be able to remove the entry).
+// The method will make copy of the passed ELF file (to shrink it to the minimum size).
+void AddNativeDebugInfoForJit(const void* handle, const std::vector<uint8_t>& symfile)
REQUIRES(Locks::native_debug_interface_lock_);
-// Notify native debugger that JITed code has been removed.
-// It also releases the associated in-memory ELF file.
-void DeleteJITCodeEntry(JITCodeEntry* entry)
- REQUIRES(Locks::native_debug_interface_lock_);
-
-// Helper method to track life-time of JITCodeEntry.
-// It registers given code address as being described by the given entry.
-void IncrementJITCodeEntryRefcount(JITCodeEntry* entry, uintptr_t code_address)
- REQUIRES(Locks::native_debug_interface_lock_);
-
-// Helper method to track life-time of JITCodeEntry.
-// It de-registers given code address as being described by the given entry.
-void DecrementJITCodeEntryRefcount(JITCodeEntry* entry, uintptr_t code_address)
- REQUIRES(Locks::native_debug_interface_lock_);
-
-// Find the registered JITCodeEntry for given code address.
-// There can be only one entry per address at any given time.
-JITCodeEntry* GetJITCodeEntry(uintptr_t code_address)
+// Notify native debugger that JITed code has been removed and free the debug info.
+void RemoveNativeDebugInfoForJit(const void* handle)
REQUIRES(Locks::native_debug_interface_lock_);
// Returns approximate memory used by all JITCodeEntries.
-size_t GetJITCodeEntryMemUsage()
+size_t GetJitNativeDebugInfoMemUsage()
REQUIRES(Locks::native_debug_interface_lock_);
} // namespace art
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 1baa613..6d99ad0 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -718,7 +718,9 @@
Runtime* runtime = Runtime::Current();
if (UNLIKELY(runtime->UseJitCompilation() && runtime->GetJit()->JitAtFirstUse())) {
// The compiler requires a ProfilingInfo object.
- ProfilingInfo::Create(thread, method, /* retry_allocation */ true);
+ ProfilingInfo::Create(thread,
+ method->GetInterfaceMethodIfProxy(kRuntimePointerSize),
+ /* retry_allocation */ true);
JitCompileTask compile_task(method, JitCompileTask::kCompile);
compile_task.Run(thread);
return;
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index c8c13cb..68a3647 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -550,10 +550,7 @@
// Notify native debugger that we are about to remove the code.
// It does nothing if we are not using native debugger.
MutexLock mu(Thread::Current(), *Locks::native_debug_interface_lock_);
- JITCodeEntry* entry = GetJITCodeEntry(reinterpret_cast<uintptr_t>(code_ptr));
- if (entry != nullptr) {
- DecrementJITCodeEntryRefcount(entry, reinterpret_cast<uintptr_t>(code_ptr));
- }
+ RemoveNativeDebugInfoForJit(code_ptr);
if (OatQuickMethodHeader::FromCodePointer(code_ptr)->IsOptimized()) {
FreeData(GetRootTable(code_ptr));
} // else this is a JNI stub without any data.
@@ -1828,7 +1825,7 @@
MutexLock mu2(Thread::Current(), *Locks::native_debug_interface_lock_);
os << "Current JIT code cache size: " << PrettySize(used_memory_for_code_) << "\n"
<< "Current JIT data cache size: " << PrettySize(used_memory_for_data_) << "\n"
- << "Current JIT mini-debug-info size: " << PrettySize(GetJITCodeEntryMemUsage()) << "\n"
+ << "Current JIT mini-debug-info size: " << PrettySize(GetJitNativeDebugInfoMemUsage()) << "\n"
<< "Current JIT capacity: " << PrettySize(current_capacity_) << "\n"
<< "Current number of JIT JNI stub entries: " << jni_stubs_map_.size() << "\n"
<< "Current number of JIT code cache entries: " << method_code_map_.size() << "\n"
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index dd7b34a..5d18b6e 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -333,7 +333,8 @@
int32_t i = kDexFileIndexStart; // Oat file is at index 0.
for (const DexFile* dex_file : dex_files) {
if (dex_file != nullptr) {
- DeregisterDexFileForNative(soa.Self(), dex_file->Begin());
+ RemoveNativeDebugInfoForDex(soa.Self(), ArrayRef<const uint8_t>(dex_file->Begin(),
+ dex_file->Size()));
// Only delete the dex file if the dex cache is not found to prevent runtime crashes if there
// are calls to DexFile.close while the ART DexFile is still in use.
if (!class_linker->IsDexFileRegistered(soa.Self(), *dex_file)) {
diff --git a/test/004-ThreadStress/run b/test/004-ThreadStress/run
index 3851d73..8004036 100755
--- a/test/004-ThreadStress/run
+++ b/test/004-ThreadStress/run
@@ -16,7 +16,13 @@
# Enable lock contention logging.
${RUN} --runtime-option -Xlockprofthreshold:10 "${@}"
+return_status1=$?
# Run locks-only mode with stack-dump lock profiling. Reduce the number of total operations from
# the default 1000 to 100.
-${RUN} --runtime-option -Xlockprofthreshold:10 --runtime-option -Xstackdumplockprofthreshold:20 "${@}" Main --locks-only -o 100
+${RUN} --runtime-option -Xlockprofthreshold:10 --runtime-option -Xstackdumplockprofthreshold:20 \
+ "${@}" Main --locks-only -o 100
+return_status2=$?
+
+# Make sure we don't silently ignore an early failure.
+(exit $return_status1) && (exit $return_status2)
diff --git a/test/018-stack-overflow/run b/test/018-stack-overflow/run
index 1a71a1a..7443bd7 100755
--- a/test/018-stack-overflow/run
+++ b/test/018-stack-overflow/run
@@ -17,7 +17,12 @@
# Run normal. This will be the debug build.
echo "libartd run."
${RUN} "${@}"
+return_status1=$?
# Run non-debug.
echo "libart run."
${RUN} "${@/#libartd.so/libart.so}"
+return_status2=$?
+
+# Make sure we don't silently ignore an early failure.
+(exit $return_status1) && (exit $return_status2)
diff --git a/test/116-nodex2oat/run b/test/116-nodex2oat/run
index 2cdb3f7..d7984ce 100755
--- a/test/116-nodex2oat/run
+++ b/test/116-nodex2oat/run
@@ -27,11 +27,17 @@
# Make sure we can run without an oat file.
echo "Run -Xnodex2oat"
${RUN} ${flags} --runtime-option -Xnodex2oat
+return_status1=$?
# Make sure we can run with the oat file.
echo "Run -Xdex2oat"
${RUN} ${flags} --runtime-option -Xdex2oat
+return_status2=$?
# Make sure we can run with the default settings.
echo "Run default"
${RUN} ${flags}
+return_status3=$?
+
+# Make sure we don't silently ignore an early failure.
+(exit $return_status1) && (exit $return_status2) && (exit $return_status3)
diff --git a/test/117-nopatchoat/run b/test/117-nopatchoat/run
index c634900..0627fe5 100755
--- a/test/117-nopatchoat/run
+++ b/test/117-nopatchoat/run
@@ -37,11 +37,17 @@
# Make sure we can run without relocation
echo "Run without dex2oat/patchoat"
${RUN} ${flags} --runtime-option -Xnodex2oat
+return_status1=$?
# Make sure we can run with the oat file.
echo "Run with dexoat/patchoat"
${RUN} ${flags} --runtime-option -Xdex2oat
+return_status2=$?
# Make sure we can run with the default settings.
echo "Run default"
${RUN} ${flags}
+return_status3=$?
+
+# Make sure we don't silently ignore an early failure.
+(exit $return_status1) && (exit $return_status2) && (exit $return_status3)
diff --git a/test/118-noimage-dex2oat/run b/test/118-noimage-dex2oat/run
index 07bdb08..e1e2577 100644
--- a/test/118-noimage-dex2oat/run
+++ b/test/118-noimage-dex2oat/run
@@ -48,15 +48,23 @@
# Make sure we can run without an oat file.
echo "Run -Xnoimage-dex2oat"
${RUN} ${flags} ${bpath_arg} --runtime-option -Xnoimage-dex2oat --runtime-option -Xnodex2oat
+return_status1=$?
# Make sure we cannot run without an oat file without fallback.
echo "Run -Xnoimage-dex2oat -Xno-dex-file-fallback"
-${RUN} ${flags} ${bpath_arg} --runtime-option -Xnoimage-dex2oat --runtime-option -Xnodex2oat --runtime-option -Xno-dex-file-fallback
+${RUN} ${flags} ${bpath_arg} --runtime-option -Xnoimage-dex2oat --runtime-option -Xnodex2oat \
+ --runtime-option -Xno-dex-file-fallback
+return_status2=$?
# Make sure we can run with the oat file.
echo "Run -Ximage-dex2oat"
${RUN} ${flags} ${bpath_arg} --runtime-option -Ximage-dex2oat
+return_status3=$?
# Make sure we can run with the default settings.
echo "Run default"
${RUN} ${flags} ${bpath_arg}
+return_status4=$?
+
+# Make sure we don't silently ignore an early failure.
+(exit $return_status1) && (exit $return_status2) && (exit $return_status3) && (exit $return_status4)
diff --git a/test/119-noimage-patchoat/run b/test/119-noimage-patchoat/run
index 02a64f8..497dc4a 100644
--- a/test/119-noimage-patchoat/run
+++ b/test/119-noimage-patchoat/run
@@ -31,16 +31,26 @@
# Make sure we can run without an image file.
echo "Run -Xnoimage-dex2oat -Xpatchoat:/system/bin/false"
-${RUN} ${flags} ${BPATH} --runtime-option -Xnoimage-dex2oat --runtime-option -Xpatchoat:${false_bin}
+${RUN} ${flags} ${BPATH} --runtime-option -Xnoimage-dex2oat \
+ --runtime-option -Xpatchoat:${false_bin}
+return_status1=$?
# Make sure we cannot run without an image file without fallback.
echo "Run -Xnoimage-dex2oat -Xpatchoat:/system/bin/false -Xno-dex-file-fallback"
-${RUN} ${flags} ${BPATH} --runtime-option -Xnoimage-dex2oat --runtime-option -Xpatchoat:${false_bin} --runtime-option -Xno-dex-file-fallback
+${RUN} ${flags} ${BPATH} --runtime-option -Xnoimage-dex2oat \
+ --runtime-option -Xpatchoat:${false_bin} --runtime-option -Xno-dex-file-fallback
+# This second run is expected to fail: invert the return status of the previous command.
+return_status2=$((! $?))
# Make sure we can run with the image file.
echo "Run -Ximage-dex2oat"
${RUN} ${flags} ${BPATH} --runtime-option -Ximage-dex2oat
+return_status3=$?
# Make sure we can run with the default settings.
echo "Run default"
${RUN} ${flags} ${BPATH}
+return_status4=$?
+
+# Make sure we don't silently ignore an early failure.
+(exit $return_status1) && (exit $return_status2) && (exit $return_status3) && (exit $return_status4)
diff --git a/test/126-miranda-multidex/run b/test/126-miranda-multidex/run
index 23c9935..abd63cb 100755
--- a/test/126-miranda-multidex/run
+++ b/test/126-miranda-multidex/run
@@ -15,7 +15,12 @@
# limitations under the License.
${RUN} $@
+return_status1=$?
# The problem was first exposed in a no-verify setting, as that changes the resolution path
# taken. Make sure we also test in that environment.
${RUN} --no-verify ${@}
+return_status2=$?
+
+# Make sure we don't silently ignore an early failure.
+(exit $return_status1) && (exit $return_status2)
diff --git a/test/137-cfi/run b/test/137-cfi/run
index adea71a..9190b1c 100755
--- a/test/137-cfi/run
+++ b/test/137-cfi/run
@@ -21,6 +21,7 @@
${RUN} "$@" -Xcompiler-option --generate-debug-info \
--runtime-option -Xjitthreshold:0 \
--args --full-signatures --args --test-local --args --test-remote
+return_status1=$?
# Test with minimal compressed debugging information.
# Check only method names (parameters are omitted to save space).
@@ -28,3 +29,7 @@
${RUN} "$@" -Xcompiler-option --generate-mini-debug-info \
--runtime-option -Xjitthreshold:0 \
--args --test-remote
+return_status2=$?
+
+# Make sure we don't silently ignore an early failure.
+(exit $return_status1) && (exit $return_status2)
diff --git a/test/1948-obsolete-const-method-handle/build b/test/1948-obsolete-const-method-handle/build
new file mode 100644
index 0000000..ac0dcd9
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/build
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright 2018 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.
+
+# Make us exit on a failure
+set -e
+
+mkdir classes
+./util-src/build-classes $PWD/classes
+
+${DX} --dex --min-sdk-version=28 --output=classes.dex classes
+
+zip $TEST_NAME.jar classes.dex
diff --git a/test/1948-obsolete-const-method-handle/expected.txt b/test/1948-obsolete-const-method-handle/expected.txt
new file mode 100644
index 0000000..a0b80a4
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/expected.txt
@@ -0,0 +1,6 @@
+Do nothing
+Hello
+transforming calling function
+Hello
+Do nothing
+Goodbye
diff --git a/test/1948-obsolete-const-method-handle/info.txt b/test/1948-obsolete-const-method-handle/info.txt
new file mode 100644
index 0000000..ef8eb69
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/info.txt
@@ -0,0 +1,6 @@
+Tests that obsolete methods work correctly in the presence of const-method-type.
+
+const-method-type and the invoke-custom/polymorphic opcodes are new in dex 39.
+It is almost impossible to get them emmited from normal java code so we don't
+have any coverage on them. To get this coverage we use ASM to build a class file
+that contains the required opcodes.
diff --git a/test/1948-obsolete-const-method-handle/run b/test/1948-obsolete-const-method-handle/run
new file mode 100755
index 0000000..9eacb4c
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2018 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.
+
+# Squash the exit status and put it in expected
+./default-run --jvmti "$@"
diff --git a/test/1948-obsolete-const-method-handle/util-src/build-classes b/test/1948-obsolete-const-method-handle/util-src/build-classes
new file mode 100755
index 0000000..1b2d79a
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/util-src/build-classes
@@ -0,0 +1,45 @@
+#!/bin/bash
+#
+# Copyright (C) 2018 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.
+
+function fail() {
+ echo Build failed: $1 1>&2
+ exit 1
+}
+
+D8=${ANDROID_HOST_OUT}/bin/d8
+if [[ ! -x "${D8}" ]]; then
+ fail "Must have a runnable d8 binary!"
+fi
+
+if [[ -z "${ANDROID_BUILD_TOP}" ]]; then
+ fail "ANDROID_BUILD_TOP is not defined. Try running 'lunch' first."
+fi
+
+SCRIPT_PATH=$( cd $(dirname $0) ; pwd -P )
+ASM_CLASSPATH="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-6.0.jar"
+SRC_PATH="${SCRIPT_PATH}/src"
+BUILD_PATH="${1:-${SCRIPT_PATH}/classes}"
+
+if [[ ! -d "${BUILD_PATH}" ]]; then
+ mkdir "$BUILD_PATH" || exit 1
+fi
+
+# Build the initial class files.
+(cd "${SRC_PATH}" && javac -cp "${ASM_CLASSPATH}" -d "${BUILD_PATH}" Main.java art/*.java art/constmethodhandle/*.java) || fail "javac error"
+# Modify the class files using ASM
+(cd "${SCRIPT_PATH}" && java -cp "${ASM_CLASSPATH}:${BUILD_PATH}" art.constmethodhandle.TestGenerator "${BUILD_PATH}" "$D8") || fail "generator failure"
+# Remove the modification classes. We don't need nor want them for the actual test.
+(cd ${BUILD_PATH} && find . -name "TestGenerator*.class" | xargs rm) || fail "Cleanup failure"
diff --git a/test/1948-obsolete-const-method-handle/util-src/info.txt b/test/1948-obsolete-const-method-handle/util-src/info.txt
new file mode 100644
index 0000000..330659a
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/util-src/info.txt
@@ -0,0 +1,7 @@
+This builds the class files for test 1948. It does this by first compiling the
+contents of src/. Then it uses TestGenerator to add additional functions to the
+TestInvoker and Test1948 classes. These additions are a new method that loads a
+constant method-handle and invokes it in TestInvoker and functions that will
+return the dex & class bytes for the redefinition that changes the target of the
+methodHandle TestInvoker loads.
+
diff --git a/test/1948-obsolete-const-method-handle/util-src/src/Main.java b/test/1948-obsolete-const-method-handle/util-src/src/Main.java
new file mode 100644
index 0000000..9a1af2b
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/util-src/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Throwable {
+ art.Test1948.run();
+ }
+}
diff --git a/test/1948-obsolete-const-method-handle/util-src/src/art/Redefinition.java b/test/1948-obsolete-const-method-handle/util-src/src/art/Redefinition.java
new file mode 100644
index 0000000..1eec70b
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/util-src/src/art/Redefinition.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+ public static final class CommonClassDefinition {
+ public final Class<?> target;
+ public final byte[] class_file_bytes;
+ public final byte[] dex_file_bytes;
+
+ public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+ this.target = target;
+ this.class_file_bytes = class_file_bytes;
+ this.dex_file_bytes = dex_file_bytes;
+ }
+ }
+
+ // A set of possible test configurations. Test should set this if they need to.
+ // This must be kept in sync with the defines in ti-agent/common_helper.cc
+ public static enum Config {
+ COMMON_REDEFINE(0),
+ COMMON_RETRANSFORM(1),
+ COMMON_TRANSFORM(2);
+
+ private final int val;
+ private Config(int val) {
+ this.val = val;
+ }
+ }
+
+ public static void setTestConfiguration(Config type) {
+ nativeSetTestConfiguration(type.val);
+ }
+
+ private static native void nativeSetTestConfiguration(int type);
+
+ // Transforms the class
+ public static native void doCommonClassRedefinition(Class<?> target,
+ byte[] classfile,
+ byte[] dexfile);
+
+ public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+ ArrayList<Class<?>> classes = new ArrayList<>();
+ ArrayList<byte[]> class_files = new ArrayList<>();
+ ArrayList<byte[]> dex_files = new ArrayList<>();
+
+ for (CommonClassDefinition d : defs) {
+ classes.add(d.target);
+ class_files.add(d.class_file_bytes);
+ dex_files.add(d.dex_file_bytes);
+ }
+ doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+ class_files.toArray(new byte[0][]),
+ dex_files.toArray(new byte[0][]));
+ }
+
+ public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+ for (CommonClassDefinition d : defs) {
+ addCommonTransformationResult(d.target.getCanonicalName(),
+ d.class_file_bytes,
+ d.dex_file_bytes);
+ }
+ }
+
+ public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+ byte[][] classfiles,
+ byte[][] dexfiles);
+ public static native void doCommonClassRetransformation(Class<?>... target);
+ public static native void setPopRetransformations(boolean pop);
+ public static native void popTransformationFor(String name);
+ public static native void enableCommonRetransformation(boolean enable);
+ public static native void addCommonTransformationResult(String target_name,
+ byte[] class_bytes,
+ byte[] dex_bytes);
+}
diff --git a/test/1948-obsolete-const-method-handle/util-src/src/art/Test1948.java b/test/1948-obsolete-const-method-handle/util-src/src/art/Test1948.java
new file mode 100644
index 0000000..f5414fb
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/util-src/src/art/Test1948.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+import art.constmethodhandle.TestInvoke;
+import java.util.*;
+import java.lang.reflect.*;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public class Test1948 {
+ // These are initialized by a method added by test_generator.
+ // They will contain the dex bytes of TestInvoker but with the method handle changed to pointing
+ // to sayBye.
+ public static final byte[] CLASS_BYTES;
+ public static final byte[] DEX_BYTES;
+ static {
+ try {
+ // TestGenerator will add functions that get the base64 string of these functions. When we
+ // compile this the functions haven't been generated yet though so just do things this way.
+ MethodHandle getClassBase64 = MethodHandles.lookup().findStatic(
+ Test1948.class, "getClassBase64", MethodType.methodType(String.class));
+ MethodHandle getDexBase64 = MethodHandles.lookup().findStatic(
+ Test1948.class, "getDexBase64", MethodType.methodType(String.class));
+ CLASS_BYTES = Base64.getDecoder().decode((String)getClassBase64.invokeExact());
+ DEX_BYTES = Base64.getDecoder().decode((String)getDexBase64.invokeExact());
+ } catch (Throwable e) {
+ throw new Error("Failed to initialize statics: ", e);
+ }
+ }
+
+ public static void run() throws Throwable {
+ // NB Because we aren't using desugar we cannot use capturing-lambda or string concat anywhere
+ // in this test! Version 9+ javac turns these into invokedynamics using bootstrap methods not
+ // currently present in android.
+ new TestInvoke().runTest(
+ new Runnable() { public void run() { System.out.println("Do nothing"); } });
+ new TestInvoke().runTest(
+ new Runnable() {
+ public void run() {
+ System.out.println("transforming calling function");
+ Redefinition.doCommonClassRedefinition(TestInvoke.class, CLASS_BYTES, DEX_BYTES);
+ }
+ });
+ new TestInvoke().runTest(
+ new Runnable() { public void run() { System.out.println("Do nothing"); } });
+ }
+}
diff --git a/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/BaseTestInvoke.java b/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/BaseTestInvoke.java
new file mode 100644
index 0000000..c38f387
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/BaseTestInvoke.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art.constmethodhandle;
+
+public class BaseTestInvoke {
+ // Simply used to make sure that everything links right when building.
+ public void runTest(Runnable preCall) {
+ throw new Error("Should not be called!");
+ }
+}
diff --git a/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/Responses.java b/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/Responses.java
new file mode 100644
index 0000000..2495647
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/Responses.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art.constmethodhandle;
+
+// We call one of these using a constant method-handle. Which one is called is changed by the
+// redefinition.
+public class Responses {
+ public static void sayHi() {
+ System.out.println("Hello");
+ }
+
+ public static void sayBye() {
+ System.out.println("Goodbye");
+ }
+}
diff --git a/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/TestGenerator.java b/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/TestGenerator.java
new file mode 100644
index 0000000..40b5cf9
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/TestGenerator.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art.constmethodhandle;
+
+import java.io.*;
+import java.util.*;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.nio.file.*;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+// This test will modify in place the compiled java files to fill in the transformed version and
+// fill in the TestInvoker.runTest function with a load-constant of a method-handle. It will use d8
+// (passed in as an argument) to create the dex we will transform TestInvoke into.
+public class TestGenerator {
+
+ public static void main(String[] args) throws IOException {
+ if (args.length != 2) {
+ throw new Error("Unable to convert class to dex without d8 binary!");
+ }
+ Path base = Paths.get(args[0]);
+ String d8Bin = args[1];
+
+ Path initTestInvoke = base.resolve(TestGenerator.class.getPackage().getName().replace('.', '/'))
+ .resolve(TestInvoke.class.getSimpleName() + ".class");
+ byte[] initClass = new FileInputStream(initTestInvoke.toFile()).readAllBytes();
+
+ // Make the initial version of TestInvoker
+ generateInvoker(initClass, "sayHi", new FileOutputStream(initTestInvoke.toFile()));
+
+ // Make the final 'class' version of testInvoker
+ ByteArrayOutputStream finalClass = new ByteArrayOutputStream();
+ generateInvoker(initClass, "sayBye", finalClass);
+
+ Path initTest1948 = base.resolve("art").resolve(art.Test1948.class.getSimpleName() + ".class");
+ byte[] finalClassBytes = finalClass.toByteArray();
+ byte[] finalDexBytes = getFinalDexBytes(d8Bin, finalClassBytes);
+ generateTestCode(
+ new FileInputStream(initTest1948.toFile()).readAllBytes(),
+ finalClassBytes,
+ finalDexBytes,
+ new FileOutputStream(initTest1948.toFile()));
+ }
+
+ // Modify the Test1948 class bytecode so it has the transformed version of TestInvoker as a string
+ // constant.
+ private static void generateTestCode(
+ byte[] initClass, byte[] transClass, byte[] transDex, OutputStream out) throws IOException {
+ ClassReader cr = new ClassReader(initClass);
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+ cr.accept(
+ new ClassVisitor(Opcodes.ASM6, cw) {
+ @Override
+ public void visitEnd() {
+ generateStringAccessorMethod(
+ cw, "getDexBase64", Base64.getEncoder().encodeToString(transDex));
+ generateStringAccessorMethod(
+ cw, "getClassBase64", Base64.getEncoder().encodeToString(transClass));
+ super.visitEnd();
+ }
+ }, 0);
+ out.write(cw.toByteArray());
+ }
+
+ // Insert a string accessor method so we can get the transformed versions of TestInvoker.
+ private static void generateStringAccessorMethod(ClassVisitor cv, String name, String ret) {
+ MethodVisitor mv = cv.visitMethod(
+ Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC,
+ name, "()Ljava/lang/String;", null, null);
+ mv.visitLdcInsn(ret);
+ mv.visitInsn(Opcodes.ARETURN);
+ mv.visitMaxs(-1, -1);
+ }
+
+ // Use d8bin to convert the classBytes into a dex file bytes. We need to do this here because we
+ // need the dex-file bytes to be used by the test class to redefine TestInvoker. We use d8 because
+ // it doesn't require setting up a directory structures or matching file names like dx does.
+ // TODO We should maybe just call d8 functions directly?
+ private static byte[] getFinalDexBytes(String d8Bin, byte[] classBytes) throws IOException {
+ Path tempDir = Files.createTempDirectory("FinalTestInvoker_Gen");
+ File tempInput = Files.createTempFile(tempDir, "temp_input_class", ".class").toFile();
+
+ OutputStream tempClassStream = new FileOutputStream(tempInput);
+ tempClassStream.write(classBytes);
+ tempClassStream.close();
+ tempClassStream = null;
+
+ Process d8Proc = new ProcessBuilder(d8Bin,
+ // Put classes.dex in the temp-dir we made.
+ "--output", tempDir.toAbsolutePath().toString(),
+ "--min-api", "28", // Allow the new invoke ops.
+ "--no-desugaring", // Don't try to be clever please.
+ tempInput.toPath().toAbsolutePath().toString())
+ .inheritIO() // Just print to stdio.
+ .start();
+ int res;
+ try {
+ res = d8Proc.waitFor();
+ } catch (Exception e) {
+ System.out.println("Failed to dex: ".concat(e.toString()));
+ e.printStackTrace();
+ res = -123;
+ }
+ tempInput.delete();
+ try {
+ if (res == 0) {
+ byte[] out = new FileInputStream(tempDir.resolve("classes.dex").toFile()).readAllBytes();
+ tempDir.resolve("classes.dex").toFile().delete();
+ return out;
+ }
+ } finally {
+ tempDir.toFile().delete();
+ }
+ throw new Error("Failed to get dex file! " + res);
+ }
+
+ private static void generateInvoker(
+ byte[] inputClass,
+ String toCall,
+ OutputStream output) throws IOException {
+ ClassReader cr = new ClassReader(inputClass);
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+ cr.accept(
+ new ClassVisitor(Opcodes.ASM6, cw) {
+ @Override
+ public void visitEnd() {
+ generateRunTest(cw, toCall);
+ super.visitEnd();
+ }
+ }, 0);
+ output.write(cw.toByteArray());
+ }
+
+ // Creates the following method:
+ // public runTest(Runnable preCall) {
+ // preCall.run();
+ // MethodHandle mh = <CONSTANT MH>;
+ // mh.invokeExact();
+ // }
+ private static void generateRunTest(ClassVisitor cv, String toCall) {
+ MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC,
+ "runTest", "(Ljava/lang/Runnable;)V", null, null);
+ MethodType mt = MethodType.methodType(Void.TYPE);
+ Handle mh = new Handle(
+ Opcodes.H_INVOKESTATIC,
+ Type.getInternalName(Responses.class),
+ toCall,
+ mt.toMethodDescriptorString(),
+ false);
+ String internalName = Type.getInternalName(Runnable.class);
+ mv.visitVarInsn(Opcodes.ALOAD, 1);
+ mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, internalName, "run", "()V", true);
+ mv.visitLdcInsn(mh);
+ mv.visitMethodInsn(
+ Opcodes.INVOKEVIRTUAL,
+ Type.getInternalName(MethodHandle.class),
+ "invokeExact",
+ "()V",
+ false);
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ }
+}
diff --git a/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/TestInvoke.java b/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/TestInvoke.java
new file mode 100644
index 0000000..eaa856e
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/TestInvoke.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art.constmethodhandle;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+
+public class TestInvoke extends BaseTestInvoke {
+ // THIS IS GENERATED BY ASM
+ // @Override
+ // public void runTest(Runnable preCall) {
+ // preCall.run();
+ // // MAGIC! Replaced with a static method handle pointing at
+ // // art.constmethodhandle.Responses.sayHi // (or art.constmethodhandle.Responses.sayBye in the
+ // // redefined version).
+ // MethodHandle handle = CONSTANT_MH<Responses.sayHi>;
+ // handle.invokeExact();
+ // }
+}
diff --git a/test/676-proxy-jit-at-first-use/expected.txt b/test/676-proxy-jit-at-first-use/expected.txt
new file mode 100644
index 0000000..6915b2f
--- /dev/null
+++ b/test/676-proxy-jit-at-first-use/expected.txt
@@ -0,0 +1 @@
+Method: public abstract void Interface.foo()
diff --git a/test/676-proxy-jit-at-first-use/info.txt b/test/676-proxy-jit-at-first-use/info.txt
new file mode 100644
index 0000000..90b683b
--- /dev/null
+++ b/test/676-proxy-jit-at-first-use/info.txt
@@ -0,0 +1 @@
+Regression test for "jit at first use" (-Xjitthreshold:0) crash for proxy methods. b/73718713
diff --git a/test/676-proxy-jit-at-first-use/run b/test/676-proxy-jit-at-first-use/run
new file mode 100644
index 0000000..16c9f76
--- /dev/null
+++ b/test/676-proxy-jit-at-first-use/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2018 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.
+
+# Enable "jit at first use" (-Xjitthreshold:0).
+# Ensure this test is not subject to unexpected code collection.
+${RUN} "${@}" --runtime-option -Xjitthreshold:0 --runtime-option -Xjitinitialsize:32M
diff --git a/test/676-proxy-jit-at-first-use/src/Main.java b/test/676-proxy-jit-at-first-use/src/Main.java
new file mode 100644
index 0000000..4ed773f
--- /dev/null
+++ b/test/676-proxy-jit-at-first-use/src/Main.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ Interface i = (Interface) Proxy.newProxyInstance(Main.class.getClassLoader(),
+ new Class<?>[] { Interface.class },
+ new Handler());
+ i.foo();
+ }
+}
+
+interface Interface {
+ void foo();
+}
+
+class Handler implements InvocationHandler {
+ public Object invoke(Object proxy, Method method, Object[] args) {
+ System.out.println("Method: " + method);
+ return null;
+ }
+}
diff --git a/test/909-attach-agent/run b/test/909-attach-agent/run
index 4a2eb34..a556bba 100755
--- a/test/909-attach-agent/run
+++ b/test/909-attach-agent/run
@@ -25,9 +25,15 @@
--android-runtime-option -Xcompiler-option \
--android-runtime-option --debuggable \
--args agent:${agent}=909-attach-agent
+return_status1=$?
./default-run "$@" --android-runtime-option -Xcompiler-option \
--android-runtime-option --debuggable \
--args agent:${agent}=909-attach-agent
+return_status2=$?
./default-run "$@" --args agent:${agent}=909-attach-agent
+return_status3=$?
+
+# Make sure we don't silently ignore an early failure.
+(exit $return_status1) && (exit $return_status2) && (exit $return_status3)
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 464449c..cf781d7 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -20,6 +20,7 @@
# Dependencies for actually running a run-test.
TEST_ART_RUN_TEST_DEPENDENCIES := \
$(HOST_OUT_EXECUTABLES)/dx \
+ $(HOST_OUT_EXECUTABLES)/d8 \
$(HOST_OUT_EXECUTABLES)/hiddenapi \
$(HOST_OUT_EXECUTABLES)/jasmin \
$(HOST_OUT_EXECUTABLES)/smali \