summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--adbconnection/adbconnection.cc364
-rw-r--r--adbconnection/adbconnection.h31
-rw-r--r--cmdline/cmdline_parser_test.cc4
-rw-r--r--cmdline/cmdline_types.h7
-rw-r--r--compiler/Android.bp1
-rw-r--r--compiler/dex/inline_method_analyser.cc8
-rw-r--r--compiler/driver/compiler_driver.cc2
-rw-r--r--compiler/driver/compiler_options.cc1
-rw-r--r--compiler/driver/compiler_options.h8
-rw-r--r--compiler/driver/compiler_options_map-inl.h6
-rw-r--r--compiler/driver/compiler_options_map.def1
-rw-r--r--compiler/optimizing/code_generator_arm64.cc25
-rw-r--r--compiler/optimizing/code_generator_arm_vixl.cc30
-rw-r--r--compiler/optimizing/code_generator_mips.cc16
-rw-r--r--compiler/optimizing/code_generator_mips64.cc14
-rw-r--r--compiler/optimizing/code_generator_vector_arm64.cc24
-rw-r--r--compiler/optimizing/code_generator_vector_arm_vixl.cc20
-rw-r--r--compiler/optimizing/code_generator_vector_mips.cc44
-rw-r--r--compiler/optimizing/code_generator_vector_mips64.cc44
-rw-r--r--compiler/optimizing/code_generator_vector_x86.cc24
-rw-r--r--compiler/optimizing/code_generator_vector_x86_64.cc24
-rw-r--r--compiler/optimizing/code_generator_x86.cc23
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc23
-rw-r--r--compiler/optimizing/data_type-inl.h2
-rw-r--r--compiler/optimizing/data_type.cc2
-rw-r--r--compiler/optimizing/data_type.h27
-rw-r--r--compiler/optimizing/graph_visualizer.cc11
-rw-r--r--compiler/optimizing/inliner.cc9
-rw-r--r--compiler/optimizing/loop_optimization.cc68
-rw-r--r--compiler/optimizing/nodes_vector.h72
-rw-r--r--compiler/optimizing/nodes_vector_test.cc168
-rw-r--r--compiler/optimizing/optimizing_compiler.cc14
-rw-r--r--compiler/optimizing/register_allocation_resolver.cc2
-rw-r--r--compiler/optimizing/register_allocator_graph_color.cc2
-rw-r--r--compiler/optimizing/register_allocator_linear_scan.cc2
-rw-r--r--compiler/utils/x86/assembler_x86.cc24
-rw-r--r--compiler/utils/x86/assembler_x86.h6
-rw-r--r--compiler/utils/x86/assembler_x86_test.cc4
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.cc23
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.h6
-rw-r--r--compiler/utils/x86_64/assembler_x86_64_test.cc5
-rw-r--r--dex2oat/Android.bp7
-rw-r--r--dex2oat/dex2oat.cc49
-rw-r--r--dex2oat/dex2oat_options.cc6
-rw-r--r--dex2oat/dex2oat_options.def2
-rw-r--r--dex2oat/dex2oat_test.cc4
-rw-r--r--dex2oat/linker/oat_writer.cc35
-rw-r--r--dexdump/Android.bp1
-rw-r--r--dexdump/dexdump.cc21
-rw-r--r--dexdump/dexdump_cfg.cc2
-rw-r--r--dexdump/dexdump_main.cc37
-rw-r--r--dexlayout/Android.bp6
-rw-r--r--dexlayout/compact_dex_writer.cc8
-rw-r--r--dexlayout/compact_dex_writer.h3
-rw-r--r--dexlayout/dex_ir.h2
-rw-r--r--dexlayout/dex_ir_builder.cc29
-rw-r--r--dexlayout/dex_ir_builder.h11
-rw-r--r--dexlayout/dex_visualize.cc4
-rw-r--r--dexlayout/dex_writer.cc130
-rw-r--r--dexlayout/dex_writer.h61
-rw-r--r--dexlayout/dexdiag.cc5
-rw-r--r--dexlayout/dexlayout.cc23
-rw-r--r--dexlayout/dexlayout.h3
-rw-r--r--dexlayout/dexlayout_main.cc52
-rw-r--r--dexlayout/dexlayout_test.cc55
-rw-r--r--dexlist/dexlist.cc25
-rw-r--r--dt_fd_forward/dt_fd_forward.cc69
-rw-r--r--dt_fd_forward/dt_fd_forward.h4
-rw-r--r--dt_fd_forward/export/fd_transport.h6
-rw-r--r--oatdump/Android.bp2
-rw-r--r--oatdump/oatdump.cc16
-rw-r--r--openjdkjvmti/Android.bp1
-rw-r--r--openjdkjvmti/fixed_up_dex_file.cc95
-rw-r--r--openjdkjvmti/fixed_up_dex_file.h3
-rw-r--r--openjdkjvmti/ti_class.cc88
-rw-r--r--openjdkjvmti/ti_class_definition.cc218
-rw-r--r--openjdkjvmti/ti_class_definition.h71
-rw-r--r--openjdkjvmti/ti_class_loader.h2
-rw-r--r--openjdkjvmti/ti_method.cc14
-rw-r--r--openjdkjvmti/ti_redefine.cc5
-rw-r--r--openjdkjvmti/ti_redefine.h2
-rw-r--r--openjdkjvmti/ti_stack.cc2
-rw-r--r--openjdkjvmti/transform.cc57
-rw-r--r--openjdkjvmti/transform.h7
-rw-r--r--patchoat/patchoat.cc277
-rw-r--r--patchoat/patchoat.h4
-rw-r--r--patchoat/patchoat_test.cc219
-rw-r--r--profman/Android.bp1
-rw-r--r--profman/profile_assistant_test.cc29
-rw-r--r--profman/profman.cc13
-rw-r--r--runtime/Android.bp26
-rw-r--r--runtime/art_method-inl.h38
-rw-r--r--runtime/art_method.cc2
-rw-r--r--runtime/art_method.h17
-rw-r--r--runtime/base/file_utils.cc2
-rw-r--r--runtime/check_reference_map_visitor.h2
-rw-r--r--runtime/class_linker.cc4
-rw-r--r--runtime/common_throws.cc2
-rw-r--r--runtime/debugger.cc14
-rw-r--r--runtime/dex/code_item_accessors-inl.h183
-rw-r--r--runtime/dex/code_item_accessors-no_art-inl.h185
-rw-r--r--runtime/dex/code_item_accessors.h2
-rw-r--r--runtime/dex/code_item_accessors_test.cc1
-rw-r--r--runtime/dex/compact_dex_file.cc2
-rw-r--r--runtime/dex/descriptors_names.cc426
-rw-r--r--runtime/dex/descriptors_names.h63
-rw-r--r--runtime/dex/dex_file.cc10
-rw-r--r--runtime/dex/dex_file.h3
-rw-r--r--runtime/dex/dex_file_annotations.cc2
-rw-r--r--runtime/dex/dex_file_exception_helpers.cc2
-rw-r--r--runtime/dex/dex_file_layout.cc2
-rw-r--r--runtime/dex/dex_file_loader.cc4
-rw-r--r--runtime/dex/dex_file_test.cc2
-rw-r--r--runtime/dex/dex_file_verifier.cc6
-rw-r--r--runtime/dex/dex_file_verifier_test.cc2
-rw-r--r--runtime/dex/dex_instruction.cc2
-rw-r--r--runtime/dex/standard_dex_file.cc2
-rw-r--r--runtime/dex/utf-inl.h (renamed from runtime/utf-inl.h)6
-rw-r--r--runtime/dex/utf.cc (renamed from runtime/utf.cc)56
-rw-r--r--runtime/dex/utf.h (renamed from runtime/utf.h)15
-rw-r--r--runtime/dex/utf_test.cc (renamed from runtime/utf_test.cc)0
-rw-r--r--runtime/entrypoints/quick/quick_throw_entrypoints.cc1
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc4
-rw-r--r--runtime/gc/heap-inl.h11
-rw-r--r--runtime/hidden_api.h182
-rw-r--r--runtime/imtable-inl.h2
-rw-r--r--runtime/instrumentation.cc15
-rw-r--r--runtime/instrumentation.h4
-rw-r--r--runtime/intern_table.cc2
-rw-r--r--runtime/intern_table_test.cc2
-rw-r--r--runtime/interpreter/interpreter.cc4
-rw-r--r--runtime/interpreter/interpreter_common.cc2
-rw-r--r--runtime/interpreter/shadow_frame.cc2
-rw-r--r--runtime/interpreter/unstarted_runtime.cc19
-rw-r--r--runtime/jdwp/jdwp_handler.cc2
-rw-r--r--runtime/jdwp_provider.h3
-rw-r--r--runtime/jit/jit.cc2
-rw-r--r--runtime/jit/jit_code_cache.cc6
-rw-r--r--runtime/jit/profile_compilation_info.cc62
-rw-r--r--runtime/jit/profile_compilation_info.h38
-rw-r--r--runtime/jit/profile_compilation_info_test.cc169
-rw-r--r--runtime/jni_internal.cc37
-rw-r--r--runtime/leb128.h32
-rw-r--r--runtime/method_handles.cc23
-rw-r--r--runtime/method_handles_test.cc382
-rw-r--r--runtime/mirror/string-inl.h2
-rw-r--r--runtime/mirror/string.cc2
-rw-r--r--runtime/monitor.cc2
-rw-r--r--runtime/native/dalvik_system_ZygoteHooks.cc28
-rw-r--r--runtime/native/java_lang_Class.cc30
-rw-r--r--runtime/native/java_lang_reflect_Field.cc13
-rw-r--r--runtime/oat_file.cc2
-rw-r--r--runtime/oat_file.h2
-rw-r--r--runtime/primitive.h58
-rw-r--r--runtime/primitive_test.cc123
-rw-r--r--runtime/quick_exception_handler.cc6
-rw-r--r--runtime/reflection.cc14
-rw-r--r--runtime/runtime.cc29
-rw-r--r--runtime/runtime_options.def2
-rw-r--r--runtime/stack.cc8
-rw-r--r--runtime/string_reference.h2
-rw-r--r--runtime/thread.cc40
-rw-r--r--runtime/thread.h6
-rw-r--r--runtime/type_lookup_table.cc2
-rw-r--r--runtime/type_lookup_table.h2
-rw-r--r--runtime/type_lookup_table_test.cc2
-rw-r--r--runtime/utils.cc53
-rw-r--r--runtime/utils.h6
-rw-r--r--runtime/vdex_file.cc57
-rw-r--r--runtime/vdex_file.h7
-rw-r--r--runtime/well_known_classes.cc3
-rw-r--r--runtime/well_known_classes.h1
-rw-r--r--test/004-ThreadStress/src-art/Main.java (renamed from test/004-ThreadStress/src/Main.java)104
-rw-r--r--test/044-proxy/src/Main.java4
-rw-r--r--test/044-proxy/src/OOMEOnDispatch.java18
-rw-r--r--test/141-class-unload/jni_unload.cc14
-rw-r--r--test/169-threadgroup-jni/expected.txt1
-rw-r--r--test/169-threadgroup-jni/info.txt1
-rw-r--r--test/169-threadgroup-jni/jni_daemon_thread.cc65
-rw-r--r--test/169-threadgroup-jni/src/Main.java39
-rw-r--r--test/466-get-live-vreg/get_live_vreg_jni.cc3
-rw-r--r--test/651-checker-int-simd-minmax/src/Main.java16
-rw-r--r--test/674-HelloWorld-Dm/expected.txt1
-rw-r--r--test/674-HelloWorld-Dm/info.txt1
-rw-r--r--test/674-HelloWorld-Dm/run17
-rw-r--r--test/674-HelloWorld-Dm/src/Main.java21
-rw-r--r--test/674-hotness-compiled/expected.txt1
-rw-r--r--test/674-hotness-compiled/info.txt1
-rwxr-xr-xtest/674-hotness-compiled/run17
-rw-r--r--test/674-hotness-compiled/src/Main.java46
-rw-r--r--test/913-heaps/expected_d8.diff20
-rw-r--r--test/959-invoke-polymorphic-accessors/src/Main.java4
-rw-r--r--test/983-source-transform-verify/expected.txt1
-rw-r--r--test/983-source-transform-verify/src/art/Test983.java4
-rw-r--r--test/Android.bp7
-rw-r--r--test/common/runtime_state.cc28
-rwxr-xr-xtest/etc/run-test-jar22
-rw-r--r--test/knownfailures.json11
-rwxr-xr-xtest/run-test3
-rw-r--r--test/testrunner/target_config.py26
-rwxr-xr-xtest/testrunner/testrunner.py2
-rw-r--r--tools/ahat/Android.mk6
-rwxr-xr-xtools/buildbot-build.sh11
-rwxr-xr-xtools/dt_fds_forward.py11
-rwxr-xr-xtools/generate-boot-image-profile.sh4
-rw-r--r--tools/hiddenapi/Android.bp1
-rw-r--r--tools/hiddenapi/hiddenapi.cc38
-rwxr-xr-xtools/jfuzz/run_jfuzz_test.py2
-rw-r--r--tools/prebuilt_libjdwp_art_failures.txt3
-rw-r--r--tools/public.libraries.buildbot.txt1
-rwxr-xr-xtools/run-jdwp-tests.sh4
212 files changed, 4271 insertions, 1877 deletions
diff --git a/Android.bp b/Android.bp
index caf4f9a325..4bcceffcd1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,6 +1,7 @@
// TODO: These should be handled with transitive static library dependencies
art_static_dependencies = [
// Note: the order is important because of static linking resolution.
+ "libdexfile",
"libziparchive",
"libnativehelper",
"libnativebridge",
diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc
index 80cfc83d3c..634d6b56df 100644
--- a/adbconnection/adbconnection.cc
+++ b/adbconnection/adbconnection.cc
@@ -42,6 +42,7 @@
#include "cutils/sockets.h"
#endif
+#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/eventfd.h>
@@ -49,17 +50,35 @@
namespace adbconnection {
+// Messages sent from the transport
using dt_fd_forward::kListenStartMessage;
using dt_fd_forward::kListenEndMessage;
using dt_fd_forward::kAcceptMessage;
using dt_fd_forward::kCloseMessage;
+// Messages sent to the transport
+using dt_fd_forward::kPerformHandshakeMessage;
+using dt_fd_forward::kSkipHandshakeMessage;
+
using android::base::StringPrintf;
+static constexpr const char kJdwpHandshake[14] = {
+ 'J', 'D', 'W', 'P', '-', 'H', 'a', 'n', 'd', 's', 'h', 'a', 'k', 'e'
+};
+
static constexpr int kEventfdLocked = 0;
static constexpr int kEventfdUnlocked = 1;
static constexpr int kControlSockSendTimeout = 10;
+static constexpr size_t kPacketHeaderLen = 11;
+static constexpr off_t kPacketSizeOff = 0;
+static constexpr off_t kPacketIdOff = 4;
+static constexpr off_t kPacketCommandSetOff = 9;
+static constexpr off_t kPacketCommandOff = 10;
+
+static constexpr uint8_t kDdmCommandSet = 199;
+static constexpr uint8_t kDdmChunkCommand = 1;
+
static AdbConnectionState* gState;
static bool IsDebuggingPossible() {
@@ -116,7 +135,12 @@ AdbConnectionState::AdbConnectionState(const std::string& agent_name)
shutting_down_(false),
agent_loaded_(false),
agent_listening_(false),
- next_ddm_id_(1) {
+ agent_has_socket_(false),
+ sent_agent_fds_(false),
+ performed_handshake_(false),
+ notified_ddm_active_(false),
+ next_ddm_id_(1),
+ started_debugger_threads_(false) {
// Setup the addr.
control_addr_.controlAddrUn.sun_family = AF_UNIX;
control_addr_len_ = sizeof(control_addr_.controlAddrUn.sun_family) + sizeof(kJdwpControlName) - 1;
@@ -151,6 +175,7 @@ struct CallbackData {
static void* CallbackFunction(void* vdata) {
std::unique_ptr<CallbackData> data(reinterpret_cast<CallbackData*>(vdata));
+ CHECK(data->this_ == gState);
art::Thread* self = art::Thread::Attach(kAdbConnectionThreadName,
true,
data->thr_);
@@ -176,6 +201,10 @@ static void* CallbackFunction(void* vdata) {
int detach_result = art::Runtime::Current()->GetJavaVM()->DetachCurrentThread();
CHECK_EQ(detach_result, 0);
+ // Get rid of the connection
+ gState = nullptr;
+ delete data->this_;
+
return nullptr;
}
@@ -224,11 +253,13 @@ void AdbConnectionState::StartDebuggerThreads() {
ScopedLocalRef<jobject> thr(soa.Env(), CreateAdbConnectionThread(soa.Self()));
pthread_t pthread;
std::unique_ptr<CallbackData> data(new CallbackData { this, soa.Env()->NewGlobalRef(thr.get()) });
+ started_debugger_threads_ = true;
int pthread_create_result = pthread_create(&pthread,
nullptr,
&CallbackFunction,
data.get());
if (pthread_create_result != 0) {
+ started_debugger_threads_ = false;
// If the create succeeded the other thread will call EndThreadBirth.
art::Runtime* runtime = art::Runtime::Current();
soa.Env()->DeleteGlobalRef(data->thr_);
@@ -245,10 +276,32 @@ static bool FlagsSet(int16_t data, int16_t flags) {
}
void AdbConnectionState::CloseFds() {
- // Lock the write_event_fd so that concurrent PublishDdms will see that the connection is closed.
- ScopedEventFdLock lk(adb_write_event_fd_);
- // shutdown(adb_connection_socket_, SHUT_RDWR);
- adb_connection_socket_.reset();
+ {
+ // Lock the write_event_fd so that concurrent PublishDdms will see that the connection is
+ // closed.
+ ScopedEventFdLock lk(adb_write_event_fd_);
+ // shutdown(adb_connection_socket_, SHUT_RDWR);
+ adb_connection_socket_.reset();
+ }
+
+ // If we didn't load anything we will need to do the handshake again.
+ performed_handshake_ = false;
+
+ // If the agent isn't loaded we might need to tell ddms code the connection is closed.
+ if (!agent_loaded_ && notified_ddm_active_) {
+ NotifyDdms(/*active*/false);
+ }
+}
+
+void AdbConnectionState::NotifyDdms(bool active) {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ DCHECK_NE(notified_ddm_active_, active);
+ notified_ddm_active_ = active;
+ if (active) {
+ art::Dbg::DdmConnected();
+ } else {
+ art::Dbg::DdmDisconnected();
+ }
}
uint32_t AdbConnectionState::NextDdmId() {
@@ -257,6 +310,13 @@ uint32_t AdbConnectionState::NextDdmId() {
}
void AdbConnectionState::PublishDdmData(uint32_t type, const art::ArrayRef<const uint8_t>& data) {
+ SendDdmPacket(NextDdmId(), DdmPacketType::kCmd, type, data);
+}
+
+void AdbConnectionState::SendDdmPacket(uint32_t id,
+ DdmPacketType packet_type,
+ uint32_t type,
+ art::ArrayRef<const uint8_t> data) {
// Get the write_event early to fail fast.
ScopedEventFdLock lk(adb_write_event_fd_);
if (adb_connection_socket_ == -1) {
@@ -276,7 +336,7 @@ void AdbConnectionState::PublishDdmData(uint32_t type, const art::ArrayRef<const
kJDWPHeaderLen // jdwp command packet size
+ sizeof(uint32_t) // Type
+ sizeof(uint32_t); // length
- std::array<uint8_t, kDdmPacketHeaderSize> pkt;
+ alignas(sizeof(uint32_t)) std::array<uint8_t, kDdmPacketHeaderSize> pkt;
uint8_t* pkt_data = pkt.data();
// Write the length first.
@@ -284,22 +344,35 @@ void AdbConnectionState::PublishDdmData(uint32_t type, const art::ArrayRef<const
pkt_data += sizeof(uint32_t);
// Write the id next;
- *reinterpret_cast<uint32_t*>(pkt_data) = htonl(NextDdmId());
+ *reinterpret_cast<uint32_t*>(pkt_data) = htonl(id);
pkt_data += sizeof(uint32_t);
// next the flags. (0 for cmd packet because DDMS).
- *(pkt_data++) = 0;
- // Now the cmd-set
- *(pkt_data++) = kJDWPDdmCmdSet;
- // Now the command
- *(pkt_data++) = kJDWPDdmCmd;
+ *(pkt_data++) = static_cast<uint8_t>(packet_type);
+ switch (packet_type) {
+ case DdmPacketType::kCmd: {
+ // Now the cmd-set
+ *(pkt_data++) = kJDWPDdmCmdSet;
+ // Now the command
+ *(pkt_data++) = kJDWPDdmCmd;
+ break;
+ }
+ case DdmPacketType::kReply: {
+ // This is the error code bytes which are all 0
+ *(pkt_data++) = 0;
+ *(pkt_data++) = 0;
+ }
+ }
+ // These are at unaligned addresses so we need to do them manually.
// now the type.
- *reinterpret_cast<uint32_t*>(pkt_data) = htonl(type);
+ uint32_t net_type = htonl(type);
+ memcpy(pkt_data, &net_type, sizeof(net_type));
pkt_data += sizeof(uint32_t);
// Now the data.size()
- *reinterpret_cast<uint32_t*>(pkt_data) = htonl(data.size());
+ uint32_t net_len = htonl(data.size());
+ memcpy(pkt_data, &net_len, sizeof(net_len));
pkt_data += sizeof(uint32_t);
static uint32_t constexpr kIovSize = 2;
@@ -327,17 +400,16 @@ void AdbConnectionState::PublishDdmData(uint32_t type, const art::ArrayRef<const
}
}
-void AdbConnectionState::SendAgentFds() {
- // TODO
+void AdbConnectionState::SendAgentFds(bool require_handshake) {
DCHECK(!sent_agent_fds_);
- char dummy = '!';
+ const char* message = require_handshake ? kPerformHandshakeMessage : kSkipHandshakeMessage;
union {
cmsghdr cm;
char buffer[CMSG_SPACE(dt_fd_forward::FdSet::kDataLength)];
} cm_un;
iovec iov;
- iov.iov_base = &dummy;
- iov.iov_len = 1;
+ iov.iov_base = const_cast<char*>(message);
+ iov.iov_len = strlen(message) + 1;
msghdr msg;
msg.msg_name = nullptr;
@@ -481,6 +553,7 @@ bool AdbConnectionState::SetupAdbConnection() {
}
void AdbConnectionState::RunPollLoop(art::Thread* self) {
+ CHECK_NE(agent_name_, "");
CHECK_EQ(self->GetState(), art::kNative);
art::Locks::mutator_lock_->AssertNotHeld(self);
self->SetState(art::kWaitingInMainDebuggerLoop);
@@ -492,6 +565,7 @@ void AdbConnectionState::RunPollLoop(art::Thread* self) {
return;
}
while (!shutting_down_ && control_sock_ != -1) {
+ bool should_listen_on_connection = !agent_has_socket_ && !sent_agent_fds_;
struct pollfd pollfds[4] = {
{ sleep_event_fd_, POLLIN, 0 },
// -1 as an fd causes it to be ignored by poll
@@ -501,8 +575,8 @@ void AdbConnectionState::RunPollLoop(art::Thread* self) {
{ (adb_connection_socket_ == -1 ? control_sock_ : -1), POLLIN | POLLRDHUP, 0 },
// if we have not loaded the agent either the adb_connection_socket_ is -1 meaning we don't
// have a real connection yet or the socket through adb needs to be listened to for incoming
- // data that the agent can handle.
- { ((!agent_has_socket_ && !sent_agent_fds_) ? adb_connection_socket_ : -1), POLLIN, 0 }
+ // data that the agent or this plugin can handle.
+ { should_listen_on_connection ? adb_connection_socket_ : -1, POLLIN | POLLRDHUP, 0 }
};
int res = TEMP_FAILURE_RETRY(poll(pollfds, 4, -1));
if (res < 0) {
@@ -528,7 +602,7 @@ void AdbConnectionState::RunPollLoop(art::Thread* self) {
if (memcmp(kListenStartMessage, buf, sizeof(kListenStartMessage)) == 0) {
agent_listening_ = true;
if (adb_connection_socket_ != -1) {
- SendAgentFds();
+ SendAgentFds(/*require_handshake*/ !performed_handshake_);
}
} else if (memcmp(kListenEndMessage, buf, sizeof(kListenEndMessage)) == 0) {
agent_listening_ = false;
@@ -538,6 +612,8 @@ void AdbConnectionState::RunPollLoop(art::Thread* self) {
} else if (memcmp(kAcceptMessage, buf, sizeof(kAcceptMessage)) == 0) {
agent_has_socket_ = true;
sent_agent_fds_ = false;
+ // We will only ever do the handshake once so reset this.
+ performed_handshake_ = false;
} else {
LOG(ERROR) << "Unknown message received from debugger! '" << std::string(buf) << "'";
}
@@ -566,7 +642,9 @@ void AdbConnectionState::RunPollLoop(art::Thread* self) {
}
if (maybe_send_fds && agent_loaded_ && agent_listening_) {
VLOG(jdwp) << "Sending fds as soon as we received them.";
- SendAgentFds();
+ // The agent was already loaded so this must be after a disconnection. Therefore have the
+ // transport perform the handshake.
+ SendAgentFds(/*require_handshake*/ true);
}
} else if (FlagsSet(control_sock_poll.revents, POLLRDHUP)) {
// The other end of the adb connection just dropped it.
@@ -578,27 +656,15 @@ void AdbConnectionState::RunPollLoop(art::Thread* self) {
} else if (FlagsSet(adb_socket_poll.revents, POLLIN)) {
DCHECK(!agent_has_socket_);
if (!agent_loaded_) {
- DCHECK(!agent_listening_);
- // TODO Should we check in some other way if we are userdebug/eng?
- CHECK(art::Dbg::IsJdwpAllowed());
- // Load the agent now!
- self->AssertNoPendingException();
- art::Runtime::Current()->AttachAgent(/* JNIEnv* */ nullptr,
- MakeAgentArg(),
- /* classloader */ nullptr,
- /*allow_non_debuggable_tooling*/ true);
- if (self->IsExceptionPending()) {
- LOG(ERROR) << "Failed to load agent " << agent_name_;
- art::ScopedObjectAccess soa(self);
- self->GetException()->Dump();
- self->ClearException();
- return;
- }
- agent_loaded_ = true;
+ HandleDataWithoutAgent(self);
} else if (agent_listening_ && !sent_agent_fds_) {
VLOG(jdwp) << "Sending agent fds again on data.";
- SendAgentFds();
+ // Agent was already loaded so it can deal with the handshake.
+ SendAgentFds(/*require_handshake*/ true);
}
+ } else if (FlagsSet(adb_socket_poll.revents, POLLRDHUP)) {
+ DCHECK(!agent_has_socket_);
+ CloseFds();
} else {
VLOG(jdwp) << "Woke up poll without anything to do!";
}
@@ -606,11 +672,205 @@ void AdbConnectionState::RunPollLoop(art::Thread* self) {
}
}
+static uint32_t ReadUint32AndAdvance(/*in-out*/uint8_t** in) {
+ uint32_t res;
+ memcpy(&res, *in, sizeof(uint32_t));
+ *in = (*in) + sizeof(uint32_t);
+ return ntohl(res);
+}
+
+void AdbConnectionState::HandleDataWithoutAgent(art::Thread* self) {
+ DCHECK(!agent_loaded_);
+ DCHECK(!agent_listening_);
+ // TODO Should we check in some other way if we are userdebug/eng?
+ CHECK(art::Dbg::IsJdwpAllowed());
+ // We try to avoid loading the agent which is expensive. First lets just perform the handshake.
+ if (!performed_handshake_) {
+ PerformHandshake();
+ return;
+ }
+ // Read the packet header to figure out if it is one we can handle. We only 'peek' into the stream
+ // to see if it's one we can handle. This doesn't change the state of the socket.
+ alignas(sizeof(uint32_t)) uint8_t packet_header[kPacketHeaderLen];
+ ssize_t res = TEMP_FAILURE_RETRY(recv(adb_connection_socket_.get(),
+ packet_header,
+ sizeof(packet_header),
+ MSG_PEEK));
+ // We want to be very careful not to change the socket state until we know we succeeded. This will
+ // let us fall-back to just loading the agent and letting it deal with everything.
+ if (res <= 0) {
+ // Close the socket. We either hit EOF or an error.
+ if (res < 0) {
+ PLOG(ERROR) << "Unable to peek into adb socket due to error. Closing socket.";
+ }
+ CloseFds();
+ return;
+ } else if (res < static_cast<int>(kPacketHeaderLen)) {
+ LOG(ERROR) << "Unable to peek into adb socket. Loading agent to handle this. Only read " << res;
+ AttachJdwpAgent(self);
+ return;
+ }
+ uint32_t full_len = ntohl(*reinterpret_cast<uint32_t*>(packet_header + kPacketSizeOff));
+ uint32_t pkt_id = ntohl(*reinterpret_cast<uint32_t*>(packet_header + kPacketIdOff));
+ uint8_t pkt_cmd_set = packet_header[kPacketCommandSetOff];
+ uint8_t pkt_cmd = packet_header[kPacketCommandOff];
+ if (pkt_cmd_set != kDdmCommandSet ||
+ pkt_cmd != kDdmChunkCommand ||
+ full_len < kPacketHeaderLen) {
+ VLOG(jdwp) << "Loading agent due to jdwp packet that cannot be handled by adbconnection.";
+ AttachJdwpAgent(self);
+ return;
+ }
+ uint32_t avail = -1;
+ res = TEMP_FAILURE_RETRY(ioctl(adb_connection_socket_.get(), FIONREAD, &avail));
+ if (res < 0) {
+ PLOG(ERROR) << "Failed to determine amount of readable data in socket! Closing connection";
+ CloseFds();
+ return;
+ } else if (avail < full_len) {
+ LOG(WARNING) << "Unable to handle ddm command in adbconnection due to insufficent data. "
+ << "Expected " << full_len << " bytes but only " << avail << " are readable. "
+ << "Loading jdwp agent to deal with this.";
+ AttachJdwpAgent(self);
+ return;
+ }
+ // Actually read the data.
+ std::vector<uint8_t> full_pkt;
+ full_pkt.resize(full_len);
+ res = TEMP_FAILURE_RETRY(recv(adb_connection_socket_.get(), full_pkt.data(), full_len, 0));
+ if (res < 0) {
+ PLOG(ERROR) << "Failed to recv data from adb connection. Closing connection";
+ CloseFds();
+ return;
+ }
+ DCHECK_EQ(memcmp(full_pkt.data(), packet_header, sizeof(packet_header)), 0);
+ size_t data_size = full_len - kPacketHeaderLen;
+ if (data_size < (sizeof(uint32_t) * 2)) {
+ // This is an error (the data isn't long enough) but to match historical behavior we need to
+ // ignore it.
+ return;
+ }
+ uint8_t* ddm_data = full_pkt.data() + kPacketHeaderLen;
+ uint32_t ddm_type = ReadUint32AndAdvance(&ddm_data);
+ uint32_t ddm_len = ReadUint32AndAdvance(&ddm_data);
+ if (ddm_len > data_size - (2 * sizeof(uint32_t))) {
+ // This is an error (the data isn't long enough) but to match historical behavior we need to
+ // ignore it.
+ return;
+ }
+
+ if (!notified_ddm_active_) {
+ NotifyDdms(/*active*/ true);
+ }
+ uint32_t reply_type;
+ std::vector<uint8_t> reply;
+ if (!art::Dbg::DdmHandleChunk(self->GetJniEnv(),
+ ddm_type,
+ art::ArrayRef<const jbyte>(reinterpret_cast<const jbyte*>(ddm_data),
+ ddm_len),
+ /*out*/&reply_type,
+ /*out*/&reply)) {
+ // To match historical behavior we don't send any response when there is no data to reply with.
+ return;
+ }
+ SendDdmPacket(pkt_id,
+ DdmPacketType::kReply,
+ reply_type,
+ art::ArrayRef<const uint8_t>(reply));
+}
+
+void AdbConnectionState::PerformHandshake() {
+ CHECK(!performed_handshake_);
+ // Check to make sure we are able to read the whole handshake.
+ uint32_t avail = -1;
+ int res = TEMP_FAILURE_RETRY(ioctl(adb_connection_socket_.get(), FIONREAD, &avail));
+ if (res < 0 || avail < sizeof(kJdwpHandshake)) {
+ if (res < 0) {
+ PLOG(ERROR) << "Failed to determine amount of readable data for handshake!";
+ }
+ LOG(WARNING) << "Closing connection to broken client.";
+ CloseFds();
+ return;
+ }
+ // Perform the handshake.
+ char handshake_msg[sizeof(kJdwpHandshake)];
+ res = TEMP_FAILURE_RETRY(recv(adb_connection_socket_.get(),
+ handshake_msg,
+ sizeof(handshake_msg),
+ MSG_DONTWAIT));
+ if (res < static_cast<int>(sizeof(kJdwpHandshake)) ||
+ strncmp(handshake_msg, kJdwpHandshake, sizeof(kJdwpHandshake)) != 0) {
+ if (res < 0) {
+ PLOG(ERROR) << "Failed to read handshake!";
+ }
+ LOG(WARNING) << "Handshake failed!";
+ CloseFds();
+ return;
+ }
+ // Send the handshake back.
+ res = TEMP_FAILURE_RETRY(send(adb_connection_socket_.get(),
+ kJdwpHandshake,
+ sizeof(kJdwpHandshake),
+ 0));
+ if (res < static_cast<int>(sizeof(kJdwpHandshake))) {
+ PLOG(ERROR) << "Failed to send jdwp-handshake response.";
+ CloseFds();
+ return;
+ }
+ performed_handshake_ = true;
+}
+
+void AdbConnectionState::AttachJdwpAgent(art::Thread* self) {
+ self->AssertNoPendingException();
+ art::Runtime::Current()->AttachAgent(/* JNIEnv */ nullptr,
+ MakeAgentArg(),
+ /* classloader */ nullptr,
+ /*allow_non_debuggable_tooling*/ true);
+ if (self->IsExceptionPending()) {
+ LOG(ERROR) << "Failed to load agent " << agent_name_;
+ art::ScopedObjectAccess soa(self);
+ self->GetException()->Dump();
+ self->ClearException();
+ return;
+ }
+ agent_loaded_ = true;
+}
+
+bool ContainsArgument(const std::string& opts, const char* arg) {
+ return opts.find(arg) != std::string::npos;
+}
+
+bool ValidateJdwpOptions(const std::string& opts) {
+ bool res = true;
+ // The adbconnection plugin requires that the jdwp agent be configured as a 'server' because that
+ // is what adb expects and otherwise we will hit a deadlock as the poll loop thread stops waiting
+ // for the fd's to be passed down.
+ if (ContainsArgument(opts, "server=n")) {
+ res = false;
+ LOG(ERROR) << "Cannot start jdwp debugging with server=n from adbconnection.";
+ }
+ // We don't start the jdwp agent until threads are already running. It is far too late to suspend
+ // everything.
+ if (ContainsArgument(opts, "suspend=y")) {
+ res = false;
+ LOG(ERROR) << "Cannot use suspend=y with late-init jdwp.";
+ }
+ return res;
+}
+
std::string AdbConnectionState::MakeAgentArg() {
- // TODO Get this from something user settable?
const std::string& opts = art::Runtime::Current()->GetJdwpOptions();
- return agent_name_ + "=" + opts + (opts.empty() ? "" : ",")
- + "transport=dt_fd_forward,address=" + std::to_string(remote_agent_control_sock_);
+ DCHECK(ValidateJdwpOptions(opts));
+ // TODO Get agent_name_ from something user settable?
+ return agent_name_ + "=" + opts + (opts.empty() ? "" : ",") +
+ "ddm_already_active=" + (notified_ddm_active_ ? "y" : "n") + "," +
+ // See the comment above for why we need to be server=y. Since the agent defaults to server=n
+ // we will add it if it wasn't already present for the convenience of the user.
+ (ContainsArgument(opts, "server=y") ? "" : "server=y,") +
+ // See the comment above for why we need to be suspend=n. Since the agent defaults to
+ // suspend=y we will add it if it wasn't already present.
+ (ContainsArgument(opts, "suspend=n") ? "" : "suspend=n") +
+ "transport=dt_fd_forward,address=" + std::to_string(remote_agent_control_sock_);
}
void AdbConnectionState::StopDebuggerThreads() {
@@ -618,24 +878,26 @@ void AdbConnectionState::StopDebuggerThreads() {
shutting_down_ = true;
// Wakeup the poll loop.
uint64_t data = 1;
- TEMP_FAILURE_RETRY(write(sleep_event_fd_, &data, sizeof(data)));
+ if (sleep_event_fd_ != -1) {
+ TEMP_FAILURE_RETRY(write(sleep_event_fd_, &data, sizeof(data)));
+ }
}
// The plugin initialization function.
extern "C" bool ArtPlugin_Initialize() REQUIRES_SHARED(art::Locks::mutator_lock_) {
DCHECK(art::Runtime::Current()->GetJdwpProvider() == art::JdwpProvider::kAdbConnection);
// TODO Provide some way for apps to set this maybe?
+ DCHECK(gState == nullptr);
gState = new AdbConnectionState(kDefaultJdwpAgentName);
- CHECK(gState != nullptr);
- return true;
+ return ValidateJdwpOptions(art::Runtime::Current()->GetJdwpOptions());
}
extern "C" bool ArtPlugin_Deinitialize() {
- CHECK(gState != nullptr);
- // Just do this a second time?
- // TODO I don't think this should be needed.
gState->StopDebuggerThreads();
- delete gState;
+ if (!gState->DebuggerThreadsStarted()) {
+ // If debugger threads were started then those threads will delete the state once they are done.
+ delete gState;
+ }
return true;
}
diff --git a/adbconnection/adbconnection.h b/adbconnection/adbconnection.h
index 28a5a05af3..04e39bf4ff 100644
--- a/adbconnection/adbconnection.h
+++ b/adbconnection/adbconnection.h
@@ -24,6 +24,7 @@
#include "android-base/unique_fd.h"
#include "base/mutex.h"
+#include "base/array_ref.h"
#include "runtime_callbacks.h"
#include <sys/socket.h>
@@ -56,6 +57,8 @@ struct AdbConnectionDebuggerController : public art::DebuggerControlCallback {
AdbConnectionState* connection_;
};
+enum class DdmPacketType : uint8_t { kReply = 0x80, kCmd = 0x00, };
+
struct AdbConnectionDdmCallback : public art::DdmCallback {
explicit AdbConnectionDdmCallback(AdbConnectionState* connection) : connection_(connection) {}
@@ -69,7 +72,7 @@ struct AdbConnectionDdmCallback : public art::DdmCallback {
class AdbConnectionState {
public:
- explicit AdbConnectionState(const std::string& agent_name);
+ explicit AdbConnectionState(const std::string& name);
// Called on the listening thread to start dealing with new input. thr is used to attach the new
// thread to the runtime.
@@ -82,6 +85,11 @@ class AdbConnectionState {
// Stops debugger threads during shutdown.
void StopDebuggerThreads();
+ // If StartDebuggerThreads was called successfully.
+ bool DebuggerThreadsStarted() {
+ return started_debugger_threads_;
+ }
+
private:
uint32_t NextDdmId();
@@ -94,10 +102,23 @@ class AdbConnectionState {
android::base::unique_fd ReadFdFromAdb();
- void SendAgentFds();
+ void SendAgentFds(bool require_handshake);
void CloseFds();
+ void HandleDataWithoutAgent(art::Thread* self);
+
+ void PerformHandshake();
+
+ void AttachJdwpAgent(art::Thread* self);
+
+ void NotifyDdms(bool active);
+
+ void SendDdmPacket(uint32_t id,
+ DdmPacketType type,
+ uint32_t ddm_type,
+ art::ArrayRef<const uint8_t> data);
+
std::string agent_name_;
AdbConnectionDebuggerController controller_;
@@ -139,8 +160,14 @@ class AdbConnectionState {
std::atomic<bool> sent_agent_fds_;
+ bool performed_handshake_;
+
+ bool notified_ddm_active_;
+
std::atomic<uint32_t> next_ddm_id_;
+ bool started_debugger_threads_;
+
socklen_t control_addr_len_;
union {
sockaddr_un controlAddrUn;
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 5d672061df..3cb9731a17 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -369,13 +369,13 @@ TEST_F(CmdlineParserTest, DISABLED_TestXGcOption) {
*/
TEST_F(CmdlineParserTest, TestJdwpProviderEmpty) {
{
- EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kInternal, "", M::JdwpProvider);
+ EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kNone, "", M::JdwpProvider);
}
} // TEST_F
TEST_F(CmdlineParserTest, TestJdwpProviderDefault) {
const char* opt_args = "-XjdwpProvider:default";
- EXPECT_SINGLE_PARSE_VALUE(JdwpProvider::kInternal, opt_args, M::JdwpProvider);
+ EXPECT_SINGLE_PARSE_VALUE(JdwpProvider::kDefaultJdwpProvider, opt_args, M::JdwpProvider);
} // TEST_F
TEST_F(CmdlineParserTest, TestJdwpProviderInternal) {
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index d0d6bfd3ce..c8be69d922 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -76,9 +76,10 @@ struct CmdlineType<JdwpProvider> : CmdlineTypeParser<JdwpProvider> {
"Example: -XjdwpProvider:none to disable JDWP\n"
"Example: -XjdwpProvider:internal for internal jdwp implementation\n"
"Example: -XjdwpProvider:adbconnection for adb connection mediated jdwp implementation\n"
- "Example: -XjdwpProvider:default for the default jdwp implementation"
- " (currently internal)\n");
- } else if (option == "internal" || option == "default") {
+ "Example: -XjdwpProvider:default for the default jdwp implementation\n");
+ } else if (option == "default") {
+ return Result::Success(JdwpProvider::kDefaultJdwpProvider);
+ } else if (option == "internal") {
return Result::Success(JdwpProvider::kInternal);
} else if (option == "adbconnection") {
return Result::Success(JdwpProvider::kAdbConnection);
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 2e60e7d658..453965947d 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -184,6 +184,7 @@ art_cc_defaults {
},
generated_sources: ["art_compiler_operator_srcs"],
shared_libs: [
+ "libdexfile",
"libbase",
"libcutils", // for atrace.
"liblzma",
diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc
index ce67b85b99..dc044c1210 100644
--- a/compiler/dex/inline_method_analyser.cc
+++ b/compiler/dex/inline_method_analyser.cc
@@ -142,7 +142,7 @@ ArtMethod* GetTargetConstructor(ArtMethod* method, const Instruction* invoke_dir
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK_EQ(invoke_direct->Opcode(), Instruction::INVOKE_DIRECT);
if (kIsDebugBuild) {
- CodeItemDataAccessor accessor(method);
+ CodeItemDataAccessor accessor(method->DexInstructionData());
DCHECK_EQ(invoke_direct->VRegC_35c(),
accessor.RegistersSize() - accessor.InsSize());
}
@@ -324,9 +324,9 @@ bool DoAnalyseConstructor(const CodeItemDataAccessor* code_item,
return false;
}
if (target_method->GetDeclaringClass()->IsObjectClass()) {
- DCHECK_EQ(CodeItemDataAccessor(target_method).begin()->Opcode(), Instruction::RETURN_VOID);
+ DCHECK_EQ(target_method->DexInstructionData().begin()->Opcode(), Instruction::RETURN_VOID);
} else {
- CodeItemDataAccessor target_code_item(target_method);
+ CodeItemDataAccessor target_code_item(target_method->DexInstructionData());
if (!target_code_item.HasCodeItem()) {
return false; // Native constructor?
}
@@ -430,7 +430,7 @@ static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT) ==
InlineMethodAnalyser::IPutVariant(Instruction::IPUT_SHORT), "iget/iput_short variant");
bool InlineMethodAnalyser::AnalyseMethodCode(ArtMethod* method, InlineMethod* result) {
- CodeItemDataAccessor code_item(method);
+ CodeItemDataAccessor code_item(method->DexInstructionData());
if (!code_item.HasCodeItem()) {
// Native or abstract.
return false;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 60537fd5c8..c617f54f55 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -932,7 +932,7 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor {
if (method->GetCodeItem() == nullptr) {
return; // native or abstract method
}
- CodeItemDataAccessor accessor(method);
+ CodeItemDataAccessor accessor(method->DexInstructionData());
if (accessor.TriesSize() == 0) {
return; // nothing to process
}
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 1780b1d7ed..2d82d79c4a 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -60,6 +60,7 @@ CompilerOptions::CompilerOptions()
dump_cfg_append_(false),
force_determinism_(false),
deduplicate_code_(true),
+ count_hotness_in_compiled_code_(false),
register_allocation_strategy_(RegisterAllocator::kRegisterAllocatorDefault),
passes_to_run_(nullptr) {
}
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 3f660293d2..18b0913430 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -274,6 +274,10 @@ class CompilerOptions FINAL {
return dump_stats_;
}
+ bool CountHotnessInCompiledCode() const {
+ return count_hotness_in_compiled_code_;
+ }
+
private:
bool ParseDumpInitFailures(const std::string& option, std::string* error_msg);
void ParseDumpCfgPasses(const StringPiece& option, UsageFn Usage);
@@ -336,6 +340,10 @@ class CompilerOptions FINAL {
// Whether code should be deduplicated.
bool deduplicate_code_;
+ // Whether compiled code should increment the hotness count of ArtMethod. Note that the increments
+ // won't be atomic for performance reasons, so we accept races, just like in interpreter.
+ bool count_hotness_in_compiled_code_;
+
RegisterAllocator::Strategy register_allocation_strategy_;
// If not null, specifies optimization passes which will be run instead of defaults.
diff --git a/compiler/driver/compiler_options_map-inl.h b/compiler/driver/compiler_options_map-inl.h
index f97ab08600..3b18db09fc 100644
--- a/compiler/driver/compiler_options_map-inl.h
+++ b/compiler/driver/compiler_options_map-inl.h
@@ -77,6 +77,9 @@ inline bool ReadCompilerOptions(Base& map, CompilerOptions* options, std::string
}
map.AssignIfExists(Base::VerboseMethods, &options->verbose_methods_);
options->deduplicate_code_ = map.GetOrDefault(Base::DeduplicateCode);
+ if (map.Exists(Base::CountHotnessInCompiledCode)) {
+ options->count_hotness_in_compiled_code_ = true;
+ }
if (map.Exists(Base::DumpTimings)) {
options->dump_timings_ = true;
@@ -137,6 +140,9 @@ inline void AddCompilerOptionsArgumentParserOptions(Builder& b) {
.WithValueMap({{"false", false}, {"true", true}})
.IntoKey(Map::DeduplicateCode)
+ .Define({"--count-hotness-in-compiled-code"})
+ .IntoKey(Map::CountHotnessInCompiledCode)
+
.Define({"--dump-timings"})
.IntoKey(Map::DumpTimings)
diff --git a/compiler/driver/compiler_options_map.def b/compiler/driver/compiler_options_map.def
index 2c56fd7974..acddae7299 100644
--- a/compiler/driver/compiler_options_map.def
+++ b/compiler/driver/compiler_options_map.def
@@ -58,6 +58,7 @@ COMPILER_OPTIONS_KEY (Unit, DumpCFGAppend)
COMPILER_OPTIONS_KEY (std::string, RegisterAllocationStrategy)
COMPILER_OPTIONS_KEY (ParseStringList<','>, VerboseMethods)
COMPILER_OPTIONS_KEY (bool, DeduplicateCode, true)
+COMPILER_OPTIONS_KEY (Unit, CountHotnessInCompiledCode)
COMPILER_OPTIONS_KEY (Unit, DumpTimings)
COMPILER_OPTIONS_KEY (Unit, DumpStats)
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 13bbffa1e3..3fd88e3e18 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -1488,6 +1488,14 @@ void CodeGeneratorARM64::GenerateFrameEntry() {
MacroAssembler* masm = GetVIXLAssembler();
__ Bind(&frame_entry_label_);
+ if (GetCompilerOptions().CountHotnessInCompiledCode()) {
+ UseScratchRegisterScope temps(masm);
+ Register temp = temps.AcquireX();
+ __ Ldrh(temp, MemOperand(kArtMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
+ __ Add(temp, temp, 1);
+ __ Strh(temp, MemOperand(kArtMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
+ }
+
bool do_overflow_check =
FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm64) || !IsLeafMethod();
if (do_overflow_check) {
@@ -1881,6 +1889,8 @@ void CodeGeneratorARM64::Load(DataType::Type type,
DCHECK_EQ(dst.Is64Bits(), DataType::Is64BitType(type));
__ Ldr(dst, src);
break;
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << type;
}
@@ -1959,6 +1969,8 @@ void CodeGeneratorARM64::LoadAcquire(HInstruction* instruction,
__ Fmov(FPRegister(dst), temp);
break;
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << type;
}
@@ -1986,6 +1998,8 @@ void CodeGeneratorARM64::Store(DataType::Type type,
DCHECK_EQ(src.Is64Bits(), DataType::Is64BitType(type));
__ Str(src, dst);
break;
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << type;
}
@@ -2063,6 +2077,8 @@ void CodeGeneratorARM64::StoreRelease(HInstruction* instruction,
}
break;
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << type;
}
@@ -3501,6 +3517,15 @@ void InstructionCodeGeneratorARM64::HandleGoto(HInstruction* got, HBasicBlock* s
HLoopInformation* info = block->GetLoopInformation();
if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
+ if (codegen_->GetCompilerOptions().CountHotnessInCompiledCode()) {
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ Register temp1 = temps.AcquireX();
+ Register temp2 = temps.AcquireX();
+ __ Ldr(temp1, MemOperand(sp, 0));
+ __ Ldrh(temp2, MemOperand(temp1, ArtMethod::HotnessCountOffset().Int32Value()));
+ __ Add(temp2, temp2, 1);
+ __ Strh(temp2, MemOperand(temp1, ArtMethod::HotnessCountOffset().Int32Value()));
+ }
GenerateSuspendCheck(info->GetSuspendCheck(), successor);
return;
}
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 577fe00dcd..704a0d3b87 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -2485,6 +2485,14 @@ void CodeGeneratorARMVIXL::GenerateFrameEntry() {
DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
__ Bind(&frame_entry_label_);
+ if (GetCompilerOptions().CountHotnessInCompiledCode()) {
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ vixl32::Register temp = temps.Acquire();
+ __ Ldrh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
+ __ Add(temp, temp, 1);
+ __ Strh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
+ }
+
if (HasEmptyFrame()) {
return;
}
@@ -2642,6 +2650,8 @@ Location InvokeDexCallingConventionVisitorARMVIXL::GetNextLocation(DataType::Typ
}
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unexpected parameter type " << type;
break;
@@ -2657,6 +2667,7 @@ Location InvokeDexCallingConventionVisitorARMVIXL::GetReturnLocation(DataType::T
case DataType::Type::kInt8:
case DataType::Type::kUint16:
case DataType::Type::kInt16:
+ case DataType::Type::kUint32:
case DataType::Type::kInt32: {
return LocationFrom(r0);
}
@@ -2665,6 +2676,7 @@ Location InvokeDexCallingConventionVisitorARMVIXL::GetReturnLocation(DataType::T
return LocationFrom(s0);
}
+ case DataType::Type::kUint64:
case DataType::Type::kInt64: {
return LocationFrom(r0, r1);
}
@@ -2786,6 +2798,16 @@ void InstructionCodeGeneratorARMVIXL::HandleGoto(HInstruction* got, HBasicBlock*
HLoopInformation* info = block->GetLoopInformation();
if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
+ if (codegen_->GetCompilerOptions().CountHotnessInCompiledCode()) {
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ vixl32::Register temp = temps.Acquire();
+ __ Push(vixl32::Register(kMethodRegister));
+ GetAssembler()->LoadFromOffset(kLoadWord, kMethodRegister, sp, kArmWordSize);
+ __ Ldrh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
+ __ Add(temp, temp, 1);
+ __ Strh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
+ __ Pop(vixl32::Register(kMethodRegister));
+ }
GenerateSuspendCheck(info->GetSuspendCheck(), successor);
return;
}
@@ -5494,6 +5516,8 @@ void InstructionCodeGeneratorARMVIXL::HandleFieldSet(HInstruction* instruction,
break;
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << field_type;
UNREACHABLE();
@@ -5738,6 +5762,8 @@ void InstructionCodeGeneratorARMVIXL::HandleFieldGet(HInstruction* instruction,
break;
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << load_type;
UNREACHABLE();
@@ -6230,6 +6256,8 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) {
break;
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << type;
UNREACHABLE();
@@ -6519,6 +6547,8 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) {
break;
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << value_type;
UNREACHABLE();
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 637698f1f1..855da2b18f 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -58,9 +58,11 @@ Location MipsReturnLocation(DataType::Type return_type) {
case DataType::Type::kInt8:
case DataType::Type::kUint16:
case DataType::Type::kInt16:
+ case DataType::Type::kUint32:
case DataType::Type::kInt32:
return Location::RegisterLocation(V0);
+ case DataType::Type::kUint64:
case DataType::Type::kInt64:
return Location::RegisterPairLocation(V0, V1);
@@ -140,6 +142,8 @@ Location InvokeDexCallingConventionVisitorMIPS::GetNextLocation(DataType::Type t
break;
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unexpected parameter type " << type;
break;
@@ -1276,6 +1280,10 @@ static dwarf::Reg DWARFReg(Register reg) {
void CodeGeneratorMIPS::GenerateFrameEntry() {
__ Bind(&frame_entry_label_);
+ if (GetCompilerOptions().CountHotnessInCompiledCode()) {
+ LOG(WARNING) << "Unimplemented hotness update in mips backend";
+ }
+
bool do_overflow_check =
FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kMips) || !IsLeafMethod();
@@ -2817,6 +2825,8 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) {
break;
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << instruction->GetType();
UNREACHABLE();
@@ -3132,6 +3142,8 @@ void InstructionCodeGeneratorMIPS::VisitArraySet(HArraySet* instruction) {
break;
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << instruction->GetType();
UNREACHABLE();
@@ -6287,6 +6299,8 @@ void InstructionCodeGeneratorMIPS::HandleFieldGet(HInstruction* instruction,
case DataType::Type::kFloat64:
load_type = kLoadDoubleword;
break;
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << type;
UNREACHABLE();
@@ -6440,6 +6454,8 @@ void InstructionCodeGeneratorMIPS::HandleFieldSet(HInstruction* instruction,
case DataType::Type::kFloat64:
store_type = kStoreDoubleword;
break;
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << type;
UNREACHABLE();
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 87ed286076..8a06061c6a 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -55,8 +55,10 @@ Location Mips64ReturnLocation(DataType::Type return_type) {
case DataType::Type::kInt8:
case DataType::Type::kUint16:
case DataType::Type::kInt16:
+ case DataType::Type::kUint32:
case DataType::Type::kInt32:
case DataType::Type::kReference:
+ case DataType::Type::kUint64:
case DataType::Type::kInt64:
return Location::RegisterLocation(V0);
@@ -1079,6 +1081,10 @@ static dwarf::Reg DWARFReg(FpuRegister reg) {
void CodeGeneratorMIPS64::GenerateFrameEntry() {
__ Bind(&frame_entry_label_);
+ if (GetCompilerOptions().CountHotnessInCompiledCode()) {
+ LOG(WARNING) << "Unimplemented hotness update in mips64 backend";
+ }
+
bool do_overflow_check =
FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kMips64) || !IsLeafMethod();
@@ -2404,6 +2410,8 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
break;
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << instruction->GetType();
UNREACHABLE();
@@ -2707,6 +2715,8 @@ void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) {
break;
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << instruction->GetType();
UNREACHABLE();
@@ -4765,6 +4775,8 @@ void InstructionCodeGeneratorMIPS64::HandleFieldGet(HInstruction* instruction,
case DataType::Type::kReference:
load_type = kLoadUnsignedWord;
break;
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << type;
UNREACHABLE();
@@ -4858,6 +4870,8 @@ void InstructionCodeGeneratorMIPS64::HandleFieldSet(HInstruction* instruction,
case DataType::Type::kFloat64:
store_type = kStoreDoubleword;
break;
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << type;
UNREACHABLE();
diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64.cc
index 152a59c208..174efdf115 100644
--- a/compiler/optimizing/code_generator_vector_arm64.cc
+++ b/compiler/optimizing/code_generator_vector_arm64.cc
@@ -606,22 +606,20 @@ void InstructionCodeGeneratorARM64::VisitVecMin(HVecMin* instruction) {
DCHECK_EQ(8u, instruction->GetVectorLength());
__ Smin(dst.V8H(), lhs.V8H(), rhs.V8H());
break;
+ case DataType::Type::kUint32:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ Umin(dst.V4S(), lhs.V4S(), rhs.V4S());
+ break;
case DataType::Type::kInt32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- if (instruction->IsUnsigned()) {
- __ Umin(dst.V4S(), lhs.V4S(), rhs.V4S());
- } else {
- __ Smin(dst.V4S(), lhs.V4S(), rhs.V4S());
- }
+ __ Smin(dst.V4S(), lhs.V4S(), rhs.V4S());
break;
case DataType::Type::kFloat32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- DCHECK(!instruction->IsUnsigned());
__ Fmin(dst.V4S(), lhs.V4S(), rhs.V4S());
break;
case DataType::Type::kFloat64:
DCHECK_EQ(2u, instruction->GetVectorLength());
- DCHECK(!instruction->IsUnsigned());
__ Fmin(dst.V2D(), lhs.V2D(), rhs.V2D());
break;
default:
@@ -656,22 +654,20 @@ void InstructionCodeGeneratorARM64::VisitVecMax(HVecMax* instruction) {
DCHECK_EQ(8u, instruction->GetVectorLength());
__ Smax(dst.V8H(), lhs.V8H(), rhs.V8H());
break;
+ case DataType::Type::kUint32:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ Umax(dst.V4S(), lhs.V4S(), rhs.V4S());
+ break;
case DataType::Type::kInt32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- if (instruction->IsUnsigned()) {
- __ Umax(dst.V4S(), lhs.V4S(), rhs.V4S());
- } else {
- __ Smax(dst.V4S(), lhs.V4S(), rhs.V4S());
- }
+ __ Smax(dst.V4S(), lhs.V4S(), rhs.V4S());
break;
case DataType::Type::kFloat32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- DCHECK(!instruction->IsUnsigned());
__ Fmax(dst.V4S(), lhs.V4S(), rhs.V4S());
break;
case DataType::Type::kFloat64:
DCHECK_EQ(2u, instruction->GetVectorLength());
- DCHECK(!instruction->IsUnsigned());
__ Fmax(dst.V2D(), lhs.V2D(), rhs.V2D());
break;
default:
diff --git a/compiler/optimizing/code_generator_vector_arm_vixl.cc b/compiler/optimizing/code_generator_vector_arm_vixl.cc
index cc470ddb2e..7c3155ab73 100644
--- a/compiler/optimizing/code_generator_vector_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_vector_arm_vixl.cc
@@ -431,13 +431,13 @@ void InstructionCodeGeneratorARMVIXL::VisitVecMin(HVecMin* instruction) {
DCHECK_EQ(4u, instruction->GetVectorLength());
__ Vmin(DataTypeValue::S16, dst, lhs, rhs);
break;
+ case DataType::Type::kUint32:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ Vmin(DataTypeValue::U32, dst, lhs, rhs);
+ break;
case DataType::Type::kInt32:
DCHECK_EQ(2u, instruction->GetVectorLength());
- if (instruction->IsUnsigned()) {
- __ Vmin(DataTypeValue::U32, dst, lhs, rhs);
- } else {
- __ Vmin(DataTypeValue::S32, dst, lhs, rhs);
- }
+ __ Vmin(DataTypeValue::S32, dst, lhs, rhs);
break;
default:
LOG(FATAL) << "Unsupported SIMD type";
@@ -471,13 +471,13 @@ void InstructionCodeGeneratorARMVIXL::VisitVecMax(HVecMax* instruction) {
DCHECK_EQ(4u, instruction->GetVectorLength());
__ Vmax(DataTypeValue::S16, dst, lhs, rhs);
break;
+ case DataType::Type::kUint32:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ Vmax(DataTypeValue::U32, dst, lhs, rhs);
+ break;
case DataType::Type::kInt32:
DCHECK_EQ(2u, instruction->GetVectorLength());
- if (instruction->IsUnsigned()) {
- __ Vmax(DataTypeValue::U32, dst, lhs, rhs);
- } else {
- __ Vmax(DataTypeValue::S32, dst, lhs, rhs);
- }
+ __ Vmax(DataTypeValue::S32, dst, lhs, rhs);
break;
default:
LOG(FATAL) << "Unsupported SIMD type";
diff --git a/compiler/optimizing/code_generator_vector_mips.cc b/compiler/optimizing/code_generator_vector_mips.cc
index 3cf150a6b8..ed9de96496 100644
--- a/compiler/optimizing/code_generator_vector_mips.cc
+++ b/compiler/optimizing/code_generator_vector_mips.cc
@@ -613,32 +613,30 @@ void InstructionCodeGeneratorMIPS::VisitVecMin(HVecMin* instruction) {
DCHECK_EQ(8u, instruction->GetVectorLength());
__ Min_sH(dst, lhs, rhs);
break;
+ case DataType::Type::kUint32:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ Min_uW(dst, lhs, rhs);
+ break;
case DataType::Type::kInt32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- if (instruction->IsUnsigned()) {
- __ Min_uW(dst, lhs, rhs);
- } else {
- __ Min_sW(dst, lhs, rhs);
- }
+ __ Min_sW(dst, lhs, rhs);
+ break;
+ case DataType::Type::kUint64:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ Min_uD(dst, lhs, rhs);
break;
case DataType::Type::kInt64:
DCHECK_EQ(2u, instruction->GetVectorLength());
- if (instruction->IsUnsigned()) {
- __ Min_uD(dst, lhs, rhs);
- } else {
- __ Min_sD(dst, lhs, rhs);
- }
+ __ Min_sD(dst, lhs, rhs);
break;
// When one of arguments is NaN, fmin.df returns other argument, but Java expects a NaN value.
// TODO: Fix min(x, NaN) cases for float and double.
case DataType::Type::kFloat32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- DCHECK(!instruction->IsUnsigned());
__ FminW(dst, lhs, rhs);
break;
case DataType::Type::kFloat64:
DCHECK_EQ(2u, instruction->GetVectorLength());
- DCHECK(!instruction->IsUnsigned());
__ FminD(dst, lhs, rhs);
break;
default:
@@ -673,32 +671,30 @@ void InstructionCodeGeneratorMIPS::VisitVecMax(HVecMax* instruction) {
DCHECK_EQ(8u, instruction->GetVectorLength());
__ Max_sH(dst, lhs, rhs);
break;
+ case DataType::Type::kUint32:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ Max_uW(dst, lhs, rhs);
+ break;
case DataType::Type::kInt32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- if (instruction->IsUnsigned()) {
- __ Max_uW(dst, lhs, rhs);
- } else {
- __ Max_sW(dst, lhs, rhs);
- }
+ __ Max_sW(dst, lhs, rhs);
+ break;
+ case DataType::Type::kUint64:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ Max_uD(dst, lhs, rhs);
break;
case DataType::Type::kInt64:
DCHECK_EQ(2u, instruction->GetVectorLength());
- if (instruction->IsUnsigned()) {
- __ Max_uD(dst, lhs, rhs);
- } else {
- __ Max_sD(dst, lhs, rhs);
- }
+ __ Max_sD(dst, lhs, rhs);
break;
// When one of arguments is NaN, fmax.df returns other argument, but Java expects a NaN value.
// TODO: Fix max(x, NaN) cases for float and double.
case DataType::Type::kFloat32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- DCHECK(!instruction->IsUnsigned());
__ FmaxW(dst, lhs, rhs);
break;
case DataType::Type::kFloat64:
DCHECK_EQ(2u, instruction->GetVectorLength());
- DCHECK(!instruction->IsUnsigned());
__ FmaxD(dst, lhs, rhs);
break;
default:
diff --git a/compiler/optimizing/code_generator_vector_mips64.cc b/compiler/optimizing/code_generator_vector_mips64.cc
index 2d69533f21..9ea55ec8d7 100644
--- a/compiler/optimizing/code_generator_vector_mips64.cc
+++ b/compiler/optimizing/code_generator_vector_mips64.cc
@@ -612,32 +612,30 @@ void InstructionCodeGeneratorMIPS64::VisitVecMin(HVecMin* instruction) {
DCHECK_EQ(8u, instruction->GetVectorLength());
__ Min_sH(dst, lhs, rhs);
break;
+ case DataType::Type::kUint32:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ Min_uW(dst, lhs, rhs);
+ break;
case DataType::Type::kInt32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- if (instruction->IsUnsigned()) {
- __ Min_uW(dst, lhs, rhs);
- } else {
- __ Min_sW(dst, lhs, rhs);
- }
+ __ Min_sW(dst, lhs, rhs);
+ break;
+ case DataType::Type::kUint64:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ Min_uD(dst, lhs, rhs);
break;
case DataType::Type::kInt64:
DCHECK_EQ(2u, instruction->GetVectorLength());
- if (instruction->IsUnsigned()) {
- __ Min_uD(dst, lhs, rhs);
- } else {
- __ Min_sD(dst, lhs, rhs);
- }
+ __ Min_sD(dst, lhs, rhs);
break;
// When one of arguments is NaN, fmin.df returns other argument, but Java expects a NaN value.
// TODO: Fix min(x, NaN) cases for float and double.
case DataType::Type::kFloat32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- DCHECK(!instruction->IsUnsigned());
__ FminW(dst, lhs, rhs);
break;
case DataType::Type::kFloat64:
DCHECK_EQ(2u, instruction->GetVectorLength());
- DCHECK(!instruction->IsUnsigned());
__ FminD(dst, lhs, rhs);
break;
default:
@@ -672,32 +670,30 @@ void InstructionCodeGeneratorMIPS64::VisitVecMax(HVecMax* instruction) {
DCHECK_EQ(8u, instruction->GetVectorLength());
__ Max_sH(dst, lhs, rhs);
break;
+ case DataType::Type::kUint32:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ Max_uW(dst, lhs, rhs);
+ break;
case DataType::Type::kInt32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- if (instruction->IsUnsigned()) {
- __ Max_uW(dst, lhs, rhs);
- } else {
- __ Max_sW(dst, lhs, rhs);
- }
+ __ Max_sW(dst, lhs, rhs);
+ break;
+ case DataType::Type::kUint64:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ Max_uD(dst, lhs, rhs);
break;
case DataType::Type::kInt64:
DCHECK_EQ(2u, instruction->GetVectorLength());
- if (instruction->IsUnsigned()) {
- __ Max_uD(dst, lhs, rhs);
- } else {
- __ Max_sD(dst, lhs, rhs);
- }
+ __ Max_sD(dst, lhs, rhs);
break;
// When one of arguments is NaN, fmax.df returns other argument, but Java expects a NaN value.
// TODO: Fix max(x, NaN) cases for float and double.
case DataType::Type::kFloat32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- DCHECK(!instruction->IsUnsigned());
__ FmaxW(dst, lhs, rhs);
break;
case DataType::Type::kFloat64:
DCHECK_EQ(2u, instruction->GetVectorLength());
- DCHECK(!instruction->IsUnsigned());
__ FmaxD(dst, lhs, rhs);
break;
default:
diff --git a/compiler/optimizing/code_generator_vector_x86.cc b/compiler/optimizing/code_generator_vector_x86.cc
index 7b4b85d2fe..f2ffccc887 100644
--- a/compiler/optimizing/code_generator_vector_x86.cc
+++ b/compiler/optimizing/code_generator_vector_x86.cc
@@ -640,23 +640,21 @@ void InstructionCodeGeneratorX86::VisitVecMin(HVecMin* instruction) {
DCHECK_EQ(8u, instruction->GetVectorLength());
__ pminsw(dst, src);
break;
+ case DataType::Type::kUint32:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ pminud(dst, src);
+ break;
case DataType::Type::kInt32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- if (instruction->IsUnsigned()) {
- __ pminud(dst, src);
- } else {
- __ pminsd(dst, src);
- }
+ __ pminsd(dst, src);
break;
// Next cases are sloppy wrt 0.0 vs -0.0.
case DataType::Type::kFloat32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- DCHECK(!instruction->IsUnsigned());
__ minps(dst, src);
break;
case DataType::Type::kFloat64:
DCHECK_EQ(2u, instruction->GetVectorLength());
- DCHECK(!instruction->IsUnsigned());
__ minpd(dst, src);
break;
default:
@@ -691,23 +689,21 @@ void InstructionCodeGeneratorX86::VisitVecMax(HVecMax* instruction) {
DCHECK_EQ(8u, instruction->GetVectorLength());
__ pmaxsw(dst, src);
break;
+ case DataType::Type::kUint32:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ pmaxud(dst, src);
+ break;
case DataType::Type::kInt32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- if (instruction->IsUnsigned()) {
- __ pmaxud(dst, src);
- } else {
- __ pmaxsd(dst, src);
- }
+ __ pmaxsd(dst, src);
break;
// Next cases are sloppy wrt 0.0 vs -0.0.
case DataType::Type::kFloat32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- DCHECK(!instruction->IsUnsigned());
__ maxps(dst, src);
break;
case DataType::Type::kFloat64:
DCHECK_EQ(2u, instruction->GetVectorLength());
- DCHECK(!instruction->IsUnsigned());
__ maxpd(dst, src);
break;
default:
diff --git a/compiler/optimizing/code_generator_vector_x86_64.cc b/compiler/optimizing/code_generator_vector_x86_64.cc
index 107030e6c2..e2b0485f89 100644
--- a/compiler/optimizing/code_generator_vector_x86_64.cc
+++ b/compiler/optimizing/code_generator_vector_x86_64.cc
@@ -623,23 +623,21 @@ void InstructionCodeGeneratorX86_64::VisitVecMin(HVecMin* instruction) {
DCHECK_EQ(8u, instruction->GetVectorLength());
__ pminsw(dst, src);
break;
+ case DataType::Type::kUint32:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ pminud(dst, src);
+ break;
case DataType::Type::kInt32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- if (instruction->IsUnsigned()) {
- __ pminud(dst, src);
- } else {
- __ pminsd(dst, src);
- }
+ __ pminsd(dst, src);
break;
// Next cases are sloppy wrt 0.0 vs -0.0.
case DataType::Type::kFloat32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- DCHECK(!instruction->IsUnsigned());
__ minps(dst, src);
break;
case DataType::Type::kFloat64:
DCHECK_EQ(2u, instruction->GetVectorLength());
- DCHECK(!instruction->IsUnsigned());
__ minpd(dst, src);
break;
default:
@@ -674,23 +672,21 @@ void InstructionCodeGeneratorX86_64::VisitVecMax(HVecMax* instruction) {
DCHECK_EQ(8u, instruction->GetVectorLength());
__ pmaxsw(dst, src);
break;
+ case DataType::Type::kUint32:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ pmaxud(dst, src);
+ break;
case DataType::Type::kInt32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- if (instruction->IsUnsigned()) {
- __ pmaxud(dst, src);
- } else {
- __ pmaxsd(dst, src);
- }
+ __ pmaxsd(dst, src);
break;
// Next cases are sloppy wrt 0.0 vs -0.0.
case DataType::Type::kFloat32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- DCHECK(!instruction->IsUnsigned());
__ maxps(dst, src);
break;
case DataType::Type::kFloat64:
DCHECK_EQ(2u, instruction->GetVectorLength());
- DCHECK(!instruction->IsUnsigned());
__ maxpd(dst, src);
break;
default:
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index cbe9e0a35c..5fede80bc7 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1061,6 +1061,11 @@ void CodeGeneratorX86::GenerateFrameEntry() {
IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86);
DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
+ if (GetCompilerOptions().CountHotnessInCompiledCode()) {
+ __ addw(Address(kMethodRegisterArgument, ArtMethod::HotnessCountOffset().Int32Value()),
+ Immediate(1));
+ }
+
if (!skip_overflow_check) {
size_t reserved_bytes = GetStackOverflowReservedBytes(InstructionSet::kX86);
__ testl(EAX, Address(ESP, -static_cast<int32_t>(reserved_bytes)));
@@ -1129,9 +1134,11 @@ Location InvokeDexCallingConventionVisitorX86::GetReturnLocation(DataType::Type
case DataType::Type::kInt8:
case DataType::Type::kUint16:
case DataType::Type::kInt16:
+ case DataType::Type::kUint32:
case DataType::Type::kInt32:
return Location::RegisterLocation(EAX);
+ case DataType::Type::kUint64:
case DataType::Type::kInt64:
return Location::RegisterPairLocation(EAX, EDX);
@@ -1201,6 +1208,8 @@ Location InvokeDexCallingConventionVisitorX86::GetNextLocation(DataType::Type ty
}
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unexpected parameter type " << type;
break;
@@ -1357,6 +1366,12 @@ void InstructionCodeGeneratorX86::HandleGoto(HInstruction* got, HBasicBlock* suc
HLoopInformation* info = block->GetLoopInformation();
if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
+ if (codegen_->GetCompilerOptions().CountHotnessInCompiledCode()) {
+ __ pushl(EAX);
+ __ movl(EAX, Address(ESP, kX86WordSize));
+ __ addw(Address(EAX, ArtMethod::HotnessCountOffset().Int32Value()), Immediate(1));
+ __ popl(EAX);
+ }
GenerateSuspendCheck(info->GetSuspendCheck(), successor);
return;
}
@@ -4833,6 +4848,8 @@ void InstructionCodeGeneratorX86::HandleFieldGet(HInstruction* instruction,
break;
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << load_type;
UNREACHABLE();
@@ -5006,6 +5023,8 @@ void InstructionCodeGeneratorX86::HandleFieldSet(HInstruction* instruction,
break;
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << field_type;
UNREACHABLE();
@@ -5309,6 +5328,8 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) {
break;
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << type;
UNREACHABLE();
@@ -5560,6 +5581,8 @@ void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) {
break;
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << instruction->GetType();
UNREACHABLE();
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 510eec4f30..ae35ab5983 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -1268,6 +1268,12 @@ void CodeGeneratorX86_64::GenerateFrameEntry() {
&& !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86_64);
DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
+ if (GetCompilerOptions().CountHotnessInCompiledCode()) {
+ __ addw(Address(CpuRegister(kMethodRegisterArgument),
+ ArtMethod::HotnessCountOffset().Int32Value()),
+ Immediate(1));
+ }
+
if (!skip_overflow_check) {
size_t reserved_bytes = GetStackOverflowReservedBytes(InstructionSet::kX86_64);
__ testq(CpuRegister(RAX), Address(CpuRegister(RSP), -static_cast<int32_t>(reserved_bytes)));
@@ -1459,6 +1465,11 @@ void InstructionCodeGeneratorX86_64::HandleGoto(HInstruction* got, HBasicBlock*
HLoopInformation* info = block->GetLoopInformation();
if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
+ if (codegen_->GetCompilerOptions().CountHotnessInCompiledCode()) {
+ __ movq(CpuRegister(TMP), Address(CpuRegister(RSP), 0));
+ __ addw(Address(CpuRegister(TMP), ArtMethod::HotnessCountOffset().Int32Value()),
+ Immediate(1));
+ }
GenerateSuspendCheck(info->GetSuspendCheck(), successor);
return;
}
@@ -2262,7 +2273,9 @@ Location InvokeDexCallingConventionVisitorX86_64::GetReturnLocation(DataType::Ty
case DataType::Type::kInt8:
case DataType::Type::kUint16:
case DataType::Type::kInt16:
+ case DataType::Type::kUint32:
case DataType::Type::kInt32:
+ case DataType::Type::kUint64:
case DataType::Type::kInt64:
return Location::RegisterLocation(RAX);
@@ -2331,6 +2344,8 @@ Location InvokeDexCallingConventionVisitorX86_64::GetNextLocation(DataType::Type
}
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unexpected parameter type " << type;
break;
@@ -4296,6 +4311,8 @@ void InstructionCodeGeneratorX86_64::HandleFieldGet(HInstruction* instruction,
break;
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << load_type;
UNREACHABLE();
@@ -4459,6 +4476,8 @@ void InstructionCodeGeneratorX86_64::HandleFieldSet(HInstruction* instruction,
break;
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << field_type;
UNREACHABLE();
@@ -4752,6 +4771,8 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
break;
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << type;
UNREACHABLE();
@@ -4991,6 +5012,8 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) {
break;
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unreachable type " << instruction->GetType();
UNREACHABLE();
diff --git a/compiler/optimizing/data_type-inl.h b/compiler/optimizing/data_type-inl.h
index e389bad3ad..e2cf7a80fe 100644
--- a/compiler/optimizing/data_type-inl.h
+++ b/compiler/optimizing/data_type-inl.h
@@ -53,7 +53,9 @@ constexpr char DataType::TypeId(DataType::Type type) {
case DataType::Type::kInt8: return 'b'; // Java byte (B).
case DataType::Type::kUint16: return 'c'; // Java char (C).
case DataType::Type::kInt16: return 's'; // Java short (S).
+ case DataType::Type::kUint32: return 'u'; // Picked 'u' for unsigned.
case DataType::Type::kInt32: return 'i'; // Java int (I).
+ case DataType::Type::kUint64: return 'w'; // Picked 'w' for long unsigned.
case DataType::Type::kInt64: return 'j'; // Java long (J).
case DataType::Type::kFloat32: return 'f'; // Java float (F).
case DataType::Type::kFloat64: return 'd'; // Java double (D).
diff --git a/compiler/optimizing/data_type.cc b/compiler/optimizing/data_type.cc
index 3c99a76c17..cb354f46cc 100644
--- a/compiler/optimizing/data_type.cc
+++ b/compiler/optimizing/data_type.cc
@@ -25,7 +25,9 @@ static const char* kTypeNames[] = {
"Int8",
"Uint16",
"Int16",
+ "Uint32",
"Int32",
+ "Uint64",
"Int64",
"Float32",
"Float64",
diff --git a/compiler/optimizing/data_type.h b/compiler/optimizing/data_type.h
index 548fe28cee..4a6c91459f 100644
--- a/compiler/optimizing/data_type.h
+++ b/compiler/optimizing/data_type.h
@@ -34,7 +34,9 @@ class DataType {
kInt8,
kUint16,
kInt16,
+ kUint32,
kInt32,
+ kUint64,
kInt64,
kFloat32,
kFloat64,
@@ -55,9 +57,11 @@ class DataType {
case Type::kUint16:
case Type::kInt16:
return 1;
+ case Type::kUint32:
case Type::kInt32:
case Type::kFloat32:
return 2;
+ case Type::kUint64:
case Type::kInt64:
case Type::kFloat64:
return 3;
@@ -80,9 +84,11 @@ class DataType {
case Type::kUint16:
case Type::kInt16:
return 2;
+ case Type::kUint32:
case Type::kInt32:
case Type::kFloat32:
return 4;
+ case Type::kUint64:
case Type::kInt64:
case Type::kFloat64:
return 8;
@@ -107,7 +113,9 @@ class DataType {
case Type::kInt8:
case Type::kUint16:
case Type::kInt16:
+ case Type::kUint32:
case Type::kInt32:
+ case Type::kUint64:
case Type::kInt64:
return true;
default:
@@ -120,11 +128,12 @@ class DataType {
}
static bool Is64BitType(Type type) {
- return type == Type::kInt64 || type == Type::kFloat64;
+ return type == Type::kUint64 || type == Type::kInt64 || type == Type::kFloat64;
}
static bool IsUnsignedType(Type type) {
- return type == Type::kBool || type == Type::kUint8 || type == Type::kUint16;
+ return type == Type::kBool || type == Type::kUint8 || type == Type::kUint16 ||
+ type == Type::kUint32 || type == Type::kUint64;
}
// Return the general kind of `type`, fusing integer-like types as Type::kInt.
@@ -133,10 +142,14 @@ class DataType {
case Type::kBool:
case Type::kUint8:
case Type::kInt8:
- case Type::kInt16:
case Type::kUint16:
+ case Type::kInt16:
+ case Type::kUint32:
case Type::kInt32:
return Type::kInt32;
+ case Type::kUint64:
+ case Type::kInt64:
+ return Type::kInt64;
default:
return type;
}
@@ -154,8 +167,12 @@ class DataType {
return std::numeric_limits<uint16_t>::min();
case Type::kInt16:
return std::numeric_limits<int16_t>::min();
+ case Type::kUint32:
+ return std::numeric_limits<uint32_t>::min();
case Type::kInt32:
return std::numeric_limits<int32_t>::min();
+ case Type::kUint64:
+ return std::numeric_limits<uint64_t>::min();
case Type::kInt64:
return std::numeric_limits<int64_t>::min();
default:
@@ -176,8 +193,12 @@ class DataType {
return std::numeric_limits<uint16_t>::max();
case Type::kInt16:
return std::numeric_limits<int16_t>::max();
+ case Type::kUint32:
+ return std::numeric_limits<uint32_t>::max();
case Type::kInt32:
return std::numeric_limits<int32_t>::max();
+ case Type::kUint64:
+ return std::numeric_limits<uint64_t>::max();
case Type::kInt64:
return std::numeric_limits<int64_t>::max();
default:
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 12c69889ab..6144162f68 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -533,20 +533,9 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
void VisitVecHalvingAdd(HVecHalvingAdd* hadd) OVERRIDE {
VisitVecBinaryOperation(hadd);
- StartAttributeStream("unsigned") << std::boolalpha << hadd->IsUnsigned() << std::noboolalpha;
StartAttributeStream("rounded") << std::boolalpha << hadd->IsRounded() << std::noboolalpha;
}
- void VisitVecMin(HVecMin* min) OVERRIDE {
- VisitVecBinaryOperation(min);
- StartAttributeStream("unsigned") << std::boolalpha << min->IsUnsigned() << std::noboolalpha;
- }
-
- void VisitVecMax(HVecMax* max) OVERRIDE {
- VisitVecBinaryOperation(max);
- StartAttributeStream("unsigned") << std::boolalpha << max->IsUnsigned() << std::noboolalpha;
- }
-
void VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instruction) OVERRIDE {
VisitVecOperation(instruction);
StartAttributeStream("kind") << instruction->GetOpKind();
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 452be6feae..035e5ce3e1 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -392,8 +392,9 @@ ArtMethod* HInliner::TryCHADevirtualization(ArtMethod* resolved_method) {
return single_impl;
}
-static bool AlwaysThrows(ArtMethod* method) {
- CodeItemDataAccessor accessor(method);
+static bool AlwaysThrows(ArtMethod* method)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ CodeItemDataAccessor accessor(method->DexInstructionData());
// Skip native methods, methods with try blocks, and methods that are too large.
if (!accessor.HasCodeItem() ||
accessor.TriesSize() != 0 ||
@@ -1418,7 +1419,7 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction,
bool same_dex_file = IsSameDexFile(*outer_compilation_unit_.GetDexFile(), *method->GetDexFile());
- CodeItemDataAccessor accessor(method);
+ CodeItemDataAccessor accessor(method->DexInstructionData());
if (!accessor.HasCodeItem()) {
LOG_FAIL_NO_STAT()
@@ -1697,7 +1698,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction,
const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
const DexFile& callee_dex_file = *resolved_method->GetDexFile();
uint32_t method_index = resolved_method->GetDexMethodIndex();
- CodeItemDebugInfoAccessor code_item_accessor(resolved_method);
+ CodeItemDebugInfoAccessor code_item_accessor(resolved_method->DexInstructionDebugInfo());
ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
Handle<mirror::DexCache> dex_cache = NewHandleIfDifferent(resolved_method->GetDexCache(),
caller_compilation_unit_.GetDexCache(),
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 3dc1ef7534..899496328e 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -30,46 +30,6 @@
namespace art {
-// TODO: Clean up the packed type detection so that we have the right type straight away
-// and do not need to go through this normalization.
-static inline void NormalizePackedType(/* inout */ DataType::Type* type,
- /* inout */ bool* is_unsigned) {
- switch (*type) {
- case DataType::Type::kBool:
- DCHECK(!*is_unsigned);
- break;
- case DataType::Type::kUint8:
- case DataType::Type::kInt8:
- if (*is_unsigned) {
- *is_unsigned = false;
- *type = DataType::Type::kUint8;
- } else {
- *type = DataType::Type::kInt8;
- }
- break;
- case DataType::Type::kUint16:
- case DataType::Type::kInt16:
- if (*is_unsigned) {
- *is_unsigned = false;
- *type = DataType::Type::kUint16;
- } else {
- *type = DataType::Type::kInt16;
- }
- break;
- case DataType::Type::kInt32:
- case DataType::Type::kInt64:
- // We do not have kUint32 and kUint64 at the moment.
- break;
- case DataType::Type::kFloat32:
- case DataType::Type::kFloat64:
- DCHECK(!*is_unsigned);
- break;
- default:
- LOG(FATAL) << "Unexpected type " << *type;
- UNREACHABLE();
- }
-}
-
// Enables vectorization (SIMDization) in the loop optimizer.
static constexpr bool kEnableVectorization = true;
@@ -1362,8 +1322,10 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node,
}
if (VectorizeUse(node, r, generate_code, type, restrictions)) {
if (generate_code) {
- NormalizePackedType(&type, &is_unsigned);
- GenerateVecOp(instruction, vector_map_->Get(r), nullptr, type);
+ GenerateVecOp(instruction,
+ vector_map_->Get(r),
+ nullptr,
+ HVecOperation::ToProperType(type, is_unsigned));
}
return true;
}
@@ -1865,18 +1827,26 @@ void HLoopOptimization::GenerateVecOp(HInstruction* org,
case Intrinsics::kMathMinLongLong:
case Intrinsics::kMathMinFloatFloat:
case Intrinsics::kMathMinDoubleDouble: {
- NormalizePackedType(&type, &is_unsigned);
vector = new (global_allocator_)
- HVecMin(global_allocator_, opa, opb, type, vector_length_, is_unsigned, dex_pc);
+ HVecMin(global_allocator_,
+ opa,
+ opb,
+ HVecOperation::ToProperType(type, is_unsigned),
+ vector_length_,
+ dex_pc);
break;
}
case Intrinsics::kMathMaxIntInt:
case Intrinsics::kMathMaxLongLong:
case Intrinsics::kMathMaxFloatFloat:
case Intrinsics::kMathMaxDoubleDouble: {
- NormalizePackedType(&type, &is_unsigned);
vector = new (global_allocator_)
- HVecMax(global_allocator_, opa, opb, type, vector_length_, is_unsigned, dex_pc);
+ HVecMax(global_allocator_,
+ opa,
+ opb,
+ HVecOperation::ToProperType(type, is_unsigned),
+ vector_length_,
+ dex_pc);
break;
}
default:
@@ -1987,15 +1957,13 @@ bool HLoopOptimization::VectorizeHalvingAddIdiom(LoopNode* node,
VectorizeUse(node, s, generate_code, type, restrictions)) {
if (generate_code) {
if (vector_mode_ == kVector) {
- NormalizePackedType(&type, &is_unsigned);
vector_map_->Put(instruction, new (global_allocator_) HVecHalvingAdd(
global_allocator_,
vector_map_->Get(r),
vector_map_->Get(s),
- type,
+ HVecOperation::ToProperType(type, is_unsigned),
vector_length_,
is_rounded,
- is_unsigned,
kNoDexPc));
MaybeRecordStat(stats_, MethodCompilationStat::kLoopVectorizedIdiom);
} else {
@@ -2086,7 +2054,7 @@ bool HLoopOptimization::VectorizeSADIdiom(LoopNode* node,
VectorizeUse(node, r, generate_code, sub_type, restrictions) &&
VectorizeUse(node, s, generate_code, sub_type, restrictions)) {
if (generate_code) {
- NormalizePackedType(&reduction_type, &is_unsigned);
+ reduction_type = HVecOperation::ToProperType(reduction_type, is_unsigned);
if (vector_mode_ == kVector) {
vector_map_->Put(instruction, new (global_allocator_) HVecSADAccumulate(
global_allocator_,
diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h
index 87dff8403b..ecabdf3b76 100644
--- a/compiler/optimizing/nodes_vector.h
+++ b/compiler/optimizing/nodes_vector.h
@@ -131,8 +131,6 @@ class HVecOperation : public HVariableInputSizeInstruction {
}
// Maps an integral type to the same-size signed type and leaves other types alone.
- // Can be used to test relaxed type consistency in which packed same-size integral
- // types can co-exist, but other type mixes are an error.
static DataType::Type ToSignedType(DataType::Type type) {
switch (type) {
case DataType::Type::kBool: // 1-byte storage unit
@@ -160,6 +158,11 @@ class HVecOperation : public HVariableInputSizeInstruction {
}
}
+ // Maps an integral type to the same-size (un)signed type. Leaves other types alone.
+ static DataType::Type ToProperType(DataType::Type type, bool is_unsigned) {
+ return is_unsigned ? ToUnsignedType(type) : ToSignedType(type);
+ }
+
// Helper method to determine if an instruction returns a SIMD value.
// TODO: This method is needed until we introduce SIMD as proper type.
static bool ReturnsSIMDValue(HInstruction* instruction) {
@@ -286,6 +289,8 @@ class HVecMemoryOperation : public HVecOperation {
};
// Packed type consistency checker ("same vector length" integral types may mix freely).
+// Tests relaxed type consistency in which packed same-size integral types can co-exist,
+// but other type mixes are an error.
inline static bool HasConsistentPackedTypes(HInstruction* input, DataType::Type type) {
if (input->IsPhi()) {
return input->GetType() == HVecOperation::kSIMDType; // carries SIMD
@@ -518,7 +523,7 @@ class HVecAdd FINAL : public HVecBinaryOperation {
// Performs halving add on every component in the two vectors, viz.
// rounded [ x1, .. , xn ] hradd [ y1, .. , yn ] = [ (x1 + y1 + 1) >> 1, .. , (xn + yn + 1) >> 1 ]
// truncated [ x1, .. , xn ] hadd [ y1, .. , yn ] = [ (x1 + y1) >> 1, .. , (xn + yn ) >> 1 ]
-// for either both signed or both unsigned operands x, y.
+// for either both signed or both unsigned operands x, y (reflected in packed_type).
class HVecHalvingAdd FINAL : public HVecBinaryOperation {
public:
HVecHalvingAdd(ArenaAllocator* allocator,
@@ -527,21 +532,13 @@ class HVecHalvingAdd FINAL : public HVecBinaryOperation {
DataType::Type packed_type,
size_t vector_length,
bool is_rounded,
- bool is_unsigned,
uint32_t dex_pc)
: HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
- // The `is_unsigned` flag should be used exclusively with the Int32 or Int64.
- // This flag is a temporary measure while we do not have the Uint32 and Uint64 data types.
- DCHECK(!is_unsigned ||
- packed_type == DataType::Type::kInt32 ||
- packed_type == DataType::Type::kInt64) << packed_type;
DCHECK(HasConsistentPackedTypes(left, packed_type));
DCHECK(HasConsistentPackedTypes(right, packed_type));
- SetPackedFlag<kFieldHAddIsUnsigned>(is_unsigned);
SetPackedFlag<kFieldHAddIsRounded>(is_rounded);
}
- bool IsUnsigned() const { return GetPackedFlag<kFieldHAddIsUnsigned>(); }
bool IsRounded() const { return GetPackedFlag<kFieldHAddIsRounded>(); }
bool CanBeMoved() const OVERRIDE { return true; }
@@ -549,9 +546,7 @@ class HVecHalvingAdd FINAL : public HVecBinaryOperation {
bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
DCHECK(other->IsVecHalvingAdd());
const HVecHalvingAdd* o = other->AsVecHalvingAdd();
- return HVecOperation::InstructionDataEquals(o) &&
- IsUnsigned() == o->IsUnsigned() &&
- IsRounded() == o->IsRounded();
+ return HVecOperation::InstructionDataEquals(o) && IsRounded() == o->IsRounded();
}
DECLARE_INSTRUCTION(VecHalvingAdd);
@@ -561,8 +556,7 @@ class HVecHalvingAdd FINAL : public HVecBinaryOperation {
private:
// Additional packed bits.
- static constexpr size_t kFieldHAddIsUnsigned = HVecOperation::kNumberOfVectorOpPackedBits;
- static constexpr size_t kFieldHAddIsRounded = kFieldHAddIsUnsigned + 1;
+ static constexpr size_t kFieldHAddIsRounded = HVecOperation::kNumberOfVectorOpPackedBits;
static constexpr size_t kNumberOfHAddPackedBits = kFieldHAddIsRounded + 1;
static_assert(kNumberOfHAddPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
};
@@ -638,7 +632,7 @@ class HVecDiv FINAL : public HVecBinaryOperation {
// Takes minimum of every component in the two vectors,
// viz. MIN( [ x1, .. , xn ] , [ y1, .. , yn ]) = [ min(x1, y1), .. , min(xn, yn) ]
-// for either both signed or both unsigned operands x, y.
+// for either both signed or both unsigned operands x, y (reflected in packed_type).
class HVecMin FINAL : public HVecBinaryOperation {
public:
HVecMin(ArenaAllocator* allocator,
@@ -646,44 +640,23 @@ class HVecMin FINAL : public HVecBinaryOperation {
HInstruction* right,
DataType::Type packed_type,
size_t vector_length,
- bool is_unsigned,
uint32_t dex_pc)
: HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
- // The `is_unsigned` flag should be used exclusively with the Int32 or Int64.
- // This flag is a temporary measure while we do not have the Uint32 and Uint64 data types.
- DCHECK(!is_unsigned ||
- packed_type == DataType::Type::kInt32 ||
- packed_type == DataType::Type::kInt64) << packed_type;
DCHECK(HasConsistentPackedTypes(left, packed_type));
DCHECK(HasConsistentPackedTypes(right, packed_type));
- SetPackedFlag<kFieldMinOpIsUnsigned>(is_unsigned);
}
- bool IsUnsigned() const { return GetPackedFlag<kFieldMinOpIsUnsigned>(); }
-
bool CanBeMoved() const OVERRIDE { return true; }
- bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
- DCHECK(other->IsVecMin());
- const HVecMin* o = other->AsVecMin();
- return HVecOperation::InstructionDataEquals(o) && IsUnsigned() == o->IsUnsigned();
- }
-
DECLARE_INSTRUCTION(VecMin);
protected:
DEFAULT_COPY_CONSTRUCTOR(VecMin);
-
- private:
- // Additional packed bits.
- static constexpr size_t kFieldMinOpIsUnsigned = HVecOperation::kNumberOfVectorOpPackedBits;
- static constexpr size_t kNumberOfMinOpPackedBits = kFieldMinOpIsUnsigned + 1;
- static_assert(kNumberOfMinOpPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
};
// Takes maximum of every component in the two vectors,
// viz. MAX( [ x1, .. , xn ] , [ y1, .. , yn ]) = [ max(x1, y1), .. , max(xn, yn) ]
-// for either both signed or both unsigned operands x, y.
+// for either both signed or both unsigned operands x, y (reflected in packed_type).
class HVecMax FINAL : public HVecBinaryOperation {
public:
HVecMax(ArenaAllocator* allocator,
@@ -691,39 +664,18 @@ class HVecMax FINAL : public HVecBinaryOperation {
HInstruction* right,
DataType::Type packed_type,
size_t vector_length,
- bool is_unsigned,
uint32_t dex_pc)
: HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
- // The `is_unsigned` flag should be used exclusively with the Int32 or Int64.
- // This flag is a temporary measure while we do not have the Uint32 and Uint64 data types.
- DCHECK(!is_unsigned ||
- packed_type == DataType::Type::kInt32 ||
- packed_type == DataType::Type::kInt64) << packed_type;
DCHECK(HasConsistentPackedTypes(left, packed_type));
DCHECK(HasConsistentPackedTypes(right, packed_type));
- SetPackedFlag<kFieldMaxOpIsUnsigned>(is_unsigned);
}
- bool IsUnsigned() const { return GetPackedFlag<kFieldMaxOpIsUnsigned>(); }
-
bool CanBeMoved() const OVERRIDE { return true; }
- bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
- DCHECK(other->IsVecMax());
- const HVecMax* o = other->AsVecMax();
- return HVecOperation::InstructionDataEquals(o) && IsUnsigned() == o->IsUnsigned();
- }
-
DECLARE_INSTRUCTION(VecMax);
protected:
DEFAULT_COPY_CONSTRUCTOR(VecMax);
-
- private:
- // Additional packed bits.
- static constexpr size_t kFieldMaxOpIsUnsigned = HVecOperation::kNumberOfVectorOpPackedBits;
- static constexpr size_t kNumberOfMaxOpPackedBits = kFieldMaxOpIsUnsigned + 1;
- static_assert(kNumberOfMaxOpPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
};
// Bitwise-ands every component in the two vectors,
diff --git a/compiler/optimizing/nodes_vector_test.cc b/compiler/optimizing/nodes_vector_test.cc
index ab9d7594d9..af13449646 100644
--- a/compiler/optimizing/nodes_vector_test.cc
+++ b/compiler/optimizing/nodes_vector_test.cc
@@ -282,143 +282,53 @@ TEST_F(NodesVectorTest, VectorAlignmentMattersOnStore) {
EXPECT_FALSE(v0->Equals(v1)); // no longer equal
}
-TEST_F(NodesVectorTest, VectorSignMattersOnMin) {
- HVecOperation* p0 = new (GetAllocator())
- HVecReplicateScalar(GetAllocator(), int32_parameter_, DataType::Type::kInt32, 4, kNoDexPc);
- HVecOperation* p1 = new (GetAllocator())
- HVecReplicateScalar(GetAllocator(), int8_parameter_, DataType::Type::kInt8, 4, kNoDexPc);
- HVecOperation* p2 = new (GetAllocator())
- HVecReplicateScalar(GetAllocator(), int16_parameter_, DataType::Type::kInt16, 4, kNoDexPc);
-
- HVecMin* v0 = new (GetAllocator()) HVecMin(
- GetAllocator(), p0, p0, DataType::Type::kInt32, 4, /*is_unsigned*/ true, kNoDexPc);
- HVecMin* v1 = new (GetAllocator()) HVecMin(
- GetAllocator(), p0, p0, DataType::Type::kInt32, 4, /*is_unsigned*/ false, kNoDexPc);
- HVecMin* v2 = new (GetAllocator()) HVecMin(
- GetAllocator(), p0, p0, DataType::Type::kInt32, 2, /*is_unsigned*/ true, kNoDexPc);
- HVecMin* v3 = new (GetAllocator()) HVecMin(
- GetAllocator(), p1, p1, DataType::Type::kUint8, 16, /*is_unsigned*/ false, kNoDexPc);
- HVecMin* v4 = new (GetAllocator()) HVecMin(
- GetAllocator(), p1, p1, DataType::Type::kInt8, 16, /*is_unsigned*/ false, kNoDexPc);
- HVecMin* v5 = new (GetAllocator()) HVecMin(
- GetAllocator(), p2, p2, DataType::Type::kUint16, 8, /*is_unsigned*/ false, kNoDexPc);
- HVecMin* v6 = new (GetAllocator()) HVecMin(
- GetAllocator(), p2, p2, DataType::Type::kInt16, 8, /*is_unsigned*/ false, kNoDexPc);
- HVecMin* min_insns[] = { v0, v1, v2, v3, v4, v5, v6 };
-
- EXPECT_FALSE(p0->CanBeMoved());
- EXPECT_FALSE(p1->CanBeMoved());
- EXPECT_FALSE(p2->CanBeMoved());
-
- for (HVecMin* min_insn : min_insns) {
- EXPECT_TRUE(min_insn->CanBeMoved());
- }
-
- // Deprecated; IsUnsigned() should be removed with the introduction of Uint32 and Uint64.
- EXPECT_TRUE(v0->IsUnsigned());
- EXPECT_FALSE(v1->IsUnsigned());
- EXPECT_TRUE(v2->IsUnsigned());
-
- for (HVecMin* min_insn1 : min_insns) {
- for (HVecMin* min_insn2 : min_insns) {
- EXPECT_EQ(min_insn1 == min_insn2, min_insn1->Equals(min_insn2));
- }
- }
-}
-
-TEST_F(NodesVectorTest, VectorSignMattersOnMax) {
- HVecOperation* p0 = new (GetAllocator())
- HVecReplicateScalar(GetAllocator(), int32_parameter_, DataType::Type::kInt32, 4, kNoDexPc);
- HVecOperation* p1 = new (GetAllocator())
- HVecReplicateScalar(GetAllocator(), int8_parameter_, DataType::Type::kInt8, 4, kNoDexPc);
- HVecOperation* p2 = new (GetAllocator())
- HVecReplicateScalar(GetAllocator(), int16_parameter_, DataType::Type::kInt16, 4, kNoDexPc);
-
- HVecMax* v0 = new (GetAllocator()) HVecMax(
- GetAllocator(), p0, p0, DataType::Type::kInt32, 4, /*is_unsigned*/ true, kNoDexPc);
- HVecMax* v1 = new (GetAllocator()) HVecMax(
- GetAllocator(), p0, p0, DataType::Type::kInt32, 4, /*is_unsigned*/ false, kNoDexPc);
- HVecMax* v2 = new (GetAllocator()) HVecMax(
- GetAllocator(), p0, p0, DataType::Type::kInt32, 2, /*is_unsigned*/ true, kNoDexPc);
- HVecMax* v3 = new (GetAllocator()) HVecMax(
- GetAllocator(), p1, p1, DataType::Type::kUint8, 16, /*is_unsigned*/ false, kNoDexPc);
- HVecMax* v4 = new (GetAllocator()) HVecMax(
- GetAllocator(), p1, p1, DataType::Type::kInt8, 16, /*is_unsigned*/ false, kNoDexPc);
- HVecMax* v5 = new (GetAllocator()) HVecMax(
- GetAllocator(), p2, p2, DataType::Type::kUint16, 8, /*is_unsigned*/ false, kNoDexPc);
- HVecMax* v6 = new (GetAllocator()) HVecMax(
- GetAllocator(), p2, p2, DataType::Type::kInt16, 8, /*is_unsigned*/ false, kNoDexPc);
- HVecMax* max_insns[] = { v0, v1, v2, v3, v4, v5, v6 };
-
- EXPECT_FALSE(p0->CanBeMoved());
- EXPECT_FALSE(p1->CanBeMoved());
- EXPECT_FALSE(p2->CanBeMoved());
-
- for (HVecMax* max_insn : max_insns) {
- EXPECT_TRUE(max_insn->CanBeMoved());
- }
-
- // Deprecated; IsUnsigned() should be removed with the introduction of Uint32 and Uint64.
- EXPECT_TRUE(v0->IsUnsigned());
- EXPECT_FALSE(v1->IsUnsigned());
- EXPECT_TRUE(v2->IsUnsigned());
-
- for (HVecMax* max_insn1 : max_insns) {
- for (HVecMax* max_insn2 : max_insns) {
- EXPECT_EQ(max_insn1 == max_insn2, max_insn1->Equals(max_insn2));
- }
- }
-}
-
TEST_F(NodesVectorTest, VectorAttributesMatterOnHalvingAdd) {
+ HVecOperation* u0 = new (GetAllocator())
+ HVecReplicateScalar(GetAllocator(), int32_parameter_, DataType::Type::kUint32, 4, kNoDexPc);
+ HVecOperation* u1 = new (GetAllocator())
+ HVecReplicateScalar(GetAllocator(), int16_parameter_, DataType::Type::kUint16, 8, kNoDexPc);
+ HVecOperation* u2 = new (GetAllocator())
+ HVecReplicateScalar(GetAllocator(), int8_parameter_, DataType::Type::kUint8, 16, kNoDexPc);
+
HVecOperation* p0 = new (GetAllocator())
HVecReplicateScalar(GetAllocator(), int32_parameter_, DataType::Type::kInt32, 4, kNoDexPc);
HVecOperation* p1 = new (GetAllocator())
- HVecReplicateScalar(GetAllocator(), int8_parameter_, DataType::Type::kInt8, 4, kNoDexPc);
+ HVecReplicateScalar(GetAllocator(), int16_parameter_, DataType::Type::kInt16, 8, kNoDexPc);
HVecOperation* p2 = new (GetAllocator())
- HVecReplicateScalar(GetAllocator(), int16_parameter_, DataType::Type::kInt16, 4, kNoDexPc);
+ HVecReplicateScalar(GetAllocator(), int8_parameter_, DataType::Type::kInt8, 16, kNoDexPc);
HVecHalvingAdd* v0 = new (GetAllocator()) HVecHalvingAdd(
- GetAllocator(), p0, p0, DataType::Type::kInt32, 4,
- /*is_rounded*/ true, /*is_unsigned*/ true, kNoDexPc);
+ GetAllocator(), u0, u0, DataType::Type::kUint32, 4, /*is_rounded*/ true, kNoDexPc);
HVecHalvingAdd* v1 = new (GetAllocator()) HVecHalvingAdd(
- GetAllocator(), p0, p0, DataType::Type::kInt32, 4,
- /*is_rounded*/ false, /*is_unsigned*/ true, kNoDexPc);
+ GetAllocator(), u0, u0, DataType::Type::kUint32, 4, /*is_rounded*/ false, kNoDexPc);
HVecHalvingAdd* v2 = new (GetAllocator()) HVecHalvingAdd(
- GetAllocator(), p0, p0, DataType::Type::kInt32, 4,
- /*is_rounded*/ true, /*is_unsigned*/ false, kNoDexPc);
+ GetAllocator(), p0, p0, DataType::Type::kInt32, 4, /*is_rounded*/ true, kNoDexPc);
HVecHalvingAdd* v3 = new (GetAllocator()) HVecHalvingAdd(
- GetAllocator(), p0, p0, DataType::Type::kInt32, 4,
- /*is_rounded*/ false, /*is_unsigned*/ false, kNoDexPc);
+ GetAllocator(), p0, p0, DataType::Type::kInt32, 4, /*is_rounded*/ false, kNoDexPc);
+
HVecHalvingAdd* v4 = new (GetAllocator()) HVecHalvingAdd(
- GetAllocator(), p0, p0, DataType::Type::kInt32, 2,
- /*is_rounded*/ true, /*is_unsigned*/ true, kNoDexPc);
+ GetAllocator(), u1, u1, DataType::Type::kUint16, 8, /*is_rounded*/ true, kNoDexPc);
HVecHalvingAdd* v5 = new (GetAllocator()) HVecHalvingAdd(
- GetAllocator(), p1, p1, DataType::Type::kUint8, 16,
- /*is_rounded*/ true, /*is_unsigned*/ false, kNoDexPc);
+ GetAllocator(), u1, u1, DataType::Type::kUint16, 8, /*is_rounded*/ false, kNoDexPc);
HVecHalvingAdd* v6 = new (GetAllocator()) HVecHalvingAdd(
- GetAllocator(), p1, p1, DataType::Type::kUint8, 16,
- /*is_rounded*/ false, /*is_unsigned*/ false, kNoDexPc);
+ GetAllocator(), p1, p1, DataType::Type::kInt16, 8, /*is_rounded*/ true, kNoDexPc);
HVecHalvingAdd* v7 = new (GetAllocator()) HVecHalvingAdd(
- GetAllocator(), p1, p1, DataType::Type::kInt8, 16,
- /*is_rounded*/ true, /*is_unsigned*/ false, kNoDexPc);
+ GetAllocator(), p1, p1, DataType::Type::kInt16, 8, /*is_rounded*/ false, kNoDexPc);
+
HVecHalvingAdd* v8 = new (GetAllocator()) HVecHalvingAdd(
- GetAllocator(), p1, p1, DataType::Type::kInt8, 16,
- /*is_rounded*/ false, /*is_unsigned*/ false, kNoDexPc);
+ GetAllocator(), u2, u2, DataType::Type::kUint8, 16, /*is_rounded*/ true, kNoDexPc);
HVecHalvingAdd* v9 = new (GetAllocator()) HVecHalvingAdd(
- GetAllocator(), p2, p2, DataType::Type::kUint16, 8,
- /*is_rounded*/ true, /*is_unsigned*/ false, kNoDexPc);
+ GetAllocator(), u2, u2, DataType::Type::kUint8, 16, /*is_rounded*/ false, kNoDexPc);
HVecHalvingAdd* v10 = new (GetAllocator()) HVecHalvingAdd(
- GetAllocator(), p2, p2, DataType::Type::kUint16, 8,
- /*is_rounded*/ false, /*is_unsigned*/ false, kNoDexPc);
+ GetAllocator(), p2, p2, DataType::Type::kInt8, 16, /*is_rounded*/ true, kNoDexPc);
HVecHalvingAdd* v11 = new (GetAllocator()) HVecHalvingAdd(
- GetAllocator(), p2, p2, DataType::Type::kInt16, 2,
- /*is_rounded*/ true, /*is_unsigned*/ false, kNoDexPc);
- HVecHalvingAdd* v12 = new (GetAllocator()) HVecHalvingAdd(
- GetAllocator(), p2, p2, DataType::Type::kInt16, 2,
- /*is_rounded*/ false, /*is_unsigned*/ false, kNoDexPc);
- HVecHalvingAdd* hadd_insns[] = { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12 };
+ GetAllocator(), p2, p2, DataType::Type::kInt8, 16, /*is_rounded*/ false, kNoDexPc);
+ HVecHalvingAdd* hadd_insns[] = { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11 };
+
+ EXPECT_FALSE(u0->CanBeMoved());
+ EXPECT_FALSE(u1->CanBeMoved());
+ EXPECT_FALSE(u2->CanBeMoved());
EXPECT_FALSE(p0->CanBeMoved());
EXPECT_FALSE(p1->CanBeMoved());
EXPECT_FALSE(p2->CanBeMoved());
@@ -427,26 +337,18 @@ TEST_F(NodesVectorTest, VectorAttributesMatterOnHalvingAdd) {
EXPECT_TRUE(hadd_insn->CanBeMoved());
}
- // Deprecated; IsUnsigned() should be removed with the introduction of Uint32 and Uint64.
- EXPECT_TRUE(v0->IsUnsigned());
- EXPECT_TRUE(v1->IsUnsigned());
- EXPECT_TRUE(!v2->IsUnsigned());
- EXPECT_TRUE(!v3->IsUnsigned());
- EXPECT_TRUE(v4->IsUnsigned());
-
EXPECT_TRUE(v0->IsRounded());
EXPECT_TRUE(!v1->IsRounded());
EXPECT_TRUE(v2->IsRounded());
EXPECT_TRUE(!v3->IsRounded());
EXPECT_TRUE(v4->IsRounded());
- EXPECT_TRUE(v5->IsRounded());
- EXPECT_TRUE(!v6->IsRounded());
- EXPECT_TRUE(v7->IsRounded());
- EXPECT_TRUE(!v8->IsRounded());
- EXPECT_TRUE(v9->IsRounded());
- EXPECT_TRUE(!v10->IsRounded());
- EXPECT_TRUE(v11->IsRounded());
- EXPECT_TRUE(!v12->IsRounded());
+ EXPECT_TRUE(!v5->IsRounded());
+ EXPECT_TRUE(v6->IsRounded());
+ EXPECT_TRUE(!v7->IsRounded());
+ EXPECT_TRUE(v8->IsRounded());
+ EXPECT_TRUE(!v9->IsRounded());
+ EXPECT_TRUE(v10->IsRounded());
+ EXPECT_TRUE(!v11->IsRounded());
for (HVecHalvingAdd* hadd_insn1 : hadd_insns) {
for (HVecHalvingAdd* hadd_insn2 : hadd_insns) {
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index c35c490118..47ef194574 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -382,7 +382,8 @@ class OptimizingCompiler FINAL : public Compiler {
PassObserver* pass_observer,
VariableSizedHandleScope* handles) const;
- void GenerateJitDebugInfo(debug::MethodDebugInfo method_debug_info);
+ void GenerateJitDebugInfo(ArtMethod* method, debug::MethodDebugInfo method_debug_info)
+ REQUIRES_SHARED(Locks::mutator_lock_);
std::unique_ptr<OptimizingCompilerStats> compilation_stats_;
@@ -1248,7 +1249,7 @@ bool OptimizingCompiler::JitCompile(Thread* self,
info.frame_size_in_bytes = method_header->GetFrameSizeInBytes();
info.code_info = nullptr;
info.cfi = jni_compiled_method.GetCfi();
- GenerateJitDebugInfo(info);
+ GenerateJitDebugInfo(method, info);
}
Runtime::Current()->GetJit()->AddMemoryUsage(method, allocator.BytesUsed());
@@ -1372,7 +1373,7 @@ bool OptimizingCompiler::JitCompile(Thread* self,
info.frame_size_in_bytes = method_header->GetFrameSizeInBytes();
info.code_info = stack_map_size == 0 ? nullptr : stack_map_data;
info.cfi = ArrayRef<const uint8_t>(*codegen->GetAssembler()->cfi().data());
- GenerateJitDebugInfo(info);
+ GenerateJitDebugInfo(method, info);
}
Runtime::Current()->GetJit()->AddMemoryUsage(method, allocator.BytesUsed());
@@ -1396,7 +1397,7 @@ bool OptimizingCompiler::JitCompile(Thread* self,
return true;
}
-void OptimizingCompiler::GenerateJitDebugInfo(debug::MethodDebugInfo info) {
+void OptimizingCompiler::GenerateJitDebugInfo(ArtMethod* method, debug::MethodDebugInfo info) {
const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions();
DCHECK(compiler_options.GenerateAnyDebugInfo());
@@ -1412,6 +1413,11 @@ void OptimizingCompiler::GenerateJitDebugInfo(debug::MethodDebugInfo info) {
MutexLock mu(Thread::Current(), g_jit_debug_mutex);
JITCodeEntry* entry = CreateJITCodeEntry(elf_file);
IncrementJITCodeEntryRefcount(entry, info.code_address);
+
+ VLOG(jit)
+ << "JIT mini-debug-info added for " << ArtMethod::PrettyMethod(method)
+ << " size=" << PrettySize(elf_file.size())
+ << " total_size=" << PrettySize(GetJITCodeEntryMemUsage());
}
} // namespace art
diff --git a/compiler/optimizing/register_allocation_resolver.cc b/compiler/optimizing/register_allocation_resolver.cc
index 1d3fe0334d..27f9ac3990 100644
--- a/compiler/optimizing/register_allocation_resolver.cc
+++ b/compiler/optimizing/register_allocation_resolver.cc
@@ -103,6 +103,7 @@ void RegisterAllocationResolver::Resolve(ArrayRef<HInstruction* const> safepoint
case DataType::Type::kFloat64:
slot += long_spill_slots;
FALLTHROUGH_INTENDED;
+ case DataType::Type::kUint64:
case DataType::Type::kInt64:
slot += float_spill_slots;
FALLTHROUGH_INTENDED;
@@ -110,6 +111,7 @@ void RegisterAllocationResolver::Resolve(ArrayRef<HInstruction* const> safepoint
slot += int_spill_slots;
FALLTHROUGH_INTENDED;
case DataType::Type::kReference:
+ case DataType::Type::kUint32:
case DataType::Type::kInt32:
case DataType::Type::kUint16:
case DataType::Type::kUint8:
diff --git a/compiler/optimizing/register_allocator_graph_color.cc b/compiler/optimizing/register_allocator_graph_color.cc
index ad5248e982..fa7ad82316 100644
--- a/compiler/optimizing/register_allocator_graph_color.cc
+++ b/compiler/optimizing/register_allocator_graph_color.cc
@@ -1972,6 +1972,8 @@ void RegisterAllocatorGraphColor::AllocateSpillSlots(ArrayRef<InterferenceNode*
case DataType::Type::kInt16:
int_intervals.push_back(parent);
break;
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unexpected type for interval " << node->GetInterval()->GetType();
UNREACHABLE();
diff --git a/compiler/optimizing/register_allocator_linear_scan.cc b/compiler/optimizing/register_allocator_linear_scan.cc
index cfe63bd758..216fb57a96 100644
--- a/compiler/optimizing/register_allocator_linear_scan.cc
+++ b/compiler/optimizing/register_allocator_linear_scan.cc
@@ -1131,6 +1131,8 @@ void RegisterAllocatorLinearScan::AllocateSpillSlotFor(LiveInterval* interval) {
case DataType::Type::kInt16:
spill_slots = &int_spill_slots_;
break;
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
case DataType::Type::kVoid:
LOG(FATAL) << "Unexpected type for interval " << interval->GetType();
}
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index 9fcede5e97..8640e2db0e 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -2100,6 +2100,14 @@ void X86Assembler::addl(const Address& address, const Immediate& imm) {
}
+void X86Assembler::addw(const Address& address, const Immediate& imm) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ CHECK(imm.is_uint16() || imm.is_int16()) << imm.value();
+ EmitUint8(0x66);
+ EmitComplex(0, address, imm, /* is_16_op */ true);
+}
+
+
void X86Assembler::adcl(Register reg, const Immediate& imm) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitComplex(2, Operand(reg), imm);
@@ -2751,14 +2759,20 @@ void X86Assembler::EmitOperand(int reg_or_opcode, const Operand& operand) {
}
-void X86Assembler::EmitImmediate(const Immediate& imm) {
- EmitInt32(imm.value());
+void X86Assembler::EmitImmediate(const Immediate& imm, bool is_16_op) {
+ if (is_16_op) {
+ EmitUint8(imm.value() & 0xFF);
+ EmitUint8(imm.value() >> 8);
+ } else {
+ EmitInt32(imm.value());
+ }
}
void X86Assembler::EmitComplex(int reg_or_opcode,
const Operand& operand,
- const Immediate& immediate) {
+ const Immediate& immediate,
+ bool is_16_op) {
CHECK_GE(reg_or_opcode, 0);
CHECK_LT(reg_or_opcode, 8);
if (immediate.is_int8()) {
@@ -2769,11 +2783,11 @@ void X86Assembler::EmitComplex(int reg_or_opcode,
} else if (operand.IsRegister(EAX)) {
// Use short form if the destination is eax.
EmitUint8(0x05 + (reg_or_opcode << 3));
- EmitImmediate(immediate);
+ EmitImmediate(immediate, is_16_op);
} else {
EmitUint8(0x81);
EmitOperand(reg_or_opcode, operand);
- EmitImmediate(immediate);
+ EmitImmediate(immediate, is_16_op);
}
}
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index f3b516cb7e..a085677083 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -634,6 +634,7 @@ class X86Assembler FINAL : public Assembler {
void addl(const Address& address, Register reg);
void addl(const Address& address, const Immediate& imm);
+ void addw(const Address& address, const Immediate& imm);
void adcl(Register dst, Register src);
void adcl(Register reg, const Immediate& imm);
@@ -817,8 +818,9 @@ class X86Assembler FINAL : public Assembler {
inline void EmitOperandSizeOverride();
void EmitOperand(int rm, const Operand& operand);
- void EmitImmediate(const Immediate& imm);
- void EmitComplex(int rm, const Operand& operand, const Immediate& immediate);
+ void EmitImmediate(const Immediate& imm, bool is_16_op = false);
+ void EmitComplex(
+ int rm, const Operand& operand, const Immediate& immediate, bool is_16_op = false);
void EmitLabel(Label* label, int instruction_size);
void EmitLabelLink(Label* label);
void EmitLabelLink(NearLabel* label);
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index 36c5c3c0c4..937dd80c4e 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -258,6 +258,10 @@ TEST_F(AssemblerX86Test, MovlLoad) {
DriverStr(RepeatRA(&x86::X86Assembler::movl, "movl {mem}, %{reg}"), "movl-load");
}
+TEST_F(AssemblerX86Test, Addw) {
+ DriverStr(RepeatAI(&x86::X86Assembler::addw, /*imm_bytes*/ 2U, "addw ${imm}, {mem}"), "addw");
+}
+
TEST_F(AssemblerX86Test, MovlStore) {
DriverStr(RepeatAR(&x86::X86Assembler::movl, "movl %{reg}, {mem}"), "movl-store");
}
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index 51f61ca756..feabf260af 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -2608,6 +2608,15 @@ void X86_64Assembler::addl(const Address& address, const Immediate& imm) {
}
+void X86_64Assembler::addw(const Address& address, const Immediate& imm) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ CHECK(imm.is_uint16() || imm.is_int16()) << imm.value();
+ EmitUint8(0x66);
+ EmitOptionalRex32(address);
+ EmitComplex(0, address, imm, /* is_16_op */ true);
+}
+
+
void X86_64Assembler::subl(CpuRegister dst, CpuRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitOptionalRex32(dst, src);
@@ -3387,8 +3396,11 @@ void X86_64Assembler::EmitOperand(uint8_t reg_or_opcode, const Operand& operand)
}
-void X86_64Assembler::EmitImmediate(const Immediate& imm) {
- if (imm.is_int32()) {
+void X86_64Assembler::EmitImmediate(const Immediate& imm, bool is_16_op) {
+ if (is_16_op) {
+ EmitUint8(imm.value() & 0xFF);
+ EmitUint8(imm.value() >> 8);
+ } else if (imm.is_int32()) {
EmitInt32(static_cast<int32_t>(imm.value()));
} else {
EmitInt64(imm.value());
@@ -3398,7 +3410,8 @@ void X86_64Assembler::EmitImmediate(const Immediate& imm) {
void X86_64Assembler::EmitComplex(uint8_t reg_or_opcode,
const Operand& operand,
- const Immediate& immediate) {
+ const Immediate& immediate,
+ bool is_16_op) {
CHECK_GE(reg_or_opcode, 0);
CHECK_LT(reg_or_opcode, 8);
if (immediate.is_int8()) {
@@ -3409,11 +3422,11 @@ void X86_64Assembler::EmitComplex(uint8_t reg_or_opcode,
} else if (operand.IsRegister(CpuRegister(RAX))) {
// Use short form if the destination is eax.
EmitUint8(0x05 + (reg_or_opcode << 3));
- EmitImmediate(immediate);
+ EmitImmediate(immediate, is_16_op);
} else {
EmitUint8(0x81);
EmitOperand(reg_or_opcode, operand);
- EmitImmediate(immediate);
+ EmitImmediate(immediate, is_16_op);
}
}
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index 0d24a751c0..7a5fdb502f 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -693,6 +693,7 @@ class X86_64Assembler FINAL : public Assembler {
void addl(CpuRegister reg, const Address& address);
void addl(const Address& address, CpuRegister reg);
void addl(const Address& address, const Immediate& imm);
+ void addw(const Address& address, const Immediate& imm);
void addq(CpuRegister reg, const Immediate& imm);
void addq(CpuRegister dst, CpuRegister src);
@@ -904,8 +905,9 @@ class X86_64Assembler FINAL : public Assembler {
void EmitOperandSizeOverride();
void EmitOperand(uint8_t rm, const Operand& operand);
- void EmitImmediate(const Immediate& imm);
- void EmitComplex(uint8_t rm, const Operand& operand, const Immediate& immediate);
+ void EmitImmediate(const Immediate& imm, bool is_16_op = false);
+ void EmitComplex(
+ uint8_t rm, const Operand& operand, const Immediate& immediate, bool is_16_op = false);
void EmitLabel(Label* label, int instruction_size);
void EmitLabelLink(Label* label);
void EmitLabelLink(NearLabel* label);
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index 0cb3ffd39f..5e6c83396a 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -578,6 +578,11 @@ TEST_F(AssemblerX86_64Test, AddlImm) {
"add ${imm}, %{reg}"), "addli");
}
+TEST_F(AssemblerX86_64Test, Addw) {
+ DriverStr(
+ RepeatAI(&x86_64::X86_64Assembler::addw, /*imm_bytes*/2U, "addw ${imm}, {mem}"), "addw");
+}
+
TEST_F(AssemblerX86_64Test, ImulqReg1) {
DriverStr(RepeatR(&x86_64::X86_64Assembler::imulq, "imulq %{reg}"), "imulq");
}
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 3a6c86d768..ab06ddda2d 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -33,8 +33,6 @@ art_cc_defaults {
},
generated_sources: ["art_dex2oat_operator_srcs"],
shared_libs: [
- "libart-compiler",
- "libart-dexlayout",
"libbase",
"liblz4",
"liblzma",
@@ -69,6 +67,7 @@ art_cc_static_library {
defaults: ["libart-dex2oat-defaults"],
shared_libs: [
"libart-compiler",
+ "libart-dexlayout",
"libart",
],
}
@@ -81,6 +80,7 @@ art_cc_static_library {
],
shared_libs: [
"libartd-compiler",
+ "libartd-dexlayout",
"libartd",
],
}
@@ -106,7 +106,6 @@ cc_defaults {
compile_multilib: "prefer32",
},
},
-
header_libs: [
"dex2oat_headers",
"art_cmdlineparser_headers",
@@ -122,6 +121,7 @@ art_cc_binary {
"libart-compiler",
"libart-dexlayout",
"libart",
+ "libdexfile",
"libbase",
"liblz4",
"libsigchain",
@@ -152,6 +152,7 @@ art_cc_binary {
"libartd-compiler",
"libartd-dexlayout",
"libartd",
+ "libdexfile",
"libbase",
"liblz4",
"libsigchain",
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 8555abf9fd..c4e53987eb 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -605,6 +605,7 @@ class Dex2Oat FINAL {
input_vdex_fd_(-1),
output_vdex_fd_(-1),
input_vdex_file_(nullptr),
+ dm_fd_(-1),
zip_fd_(-1),
image_base_(0U),
image_classes_zip_filename_(nullptr),
@@ -757,6 +758,11 @@ class Dex2Oat FINAL {
Usage("--oat-fd should not be used with --image");
}
+ if ((input_vdex_fd_ != -1 || !input_vdex_.empty()) &&
+ (dm_fd_ != -1 || !dm_file_location_.empty())) {
+ Usage("An input vdex should not be passed with a .dm file");
+ }
+
if (!parser_options->oat_symbols.empty() &&
parser_options->oat_symbols.size() != oat_filenames_.size()) {
Usage("--oat-file arguments do not match --oat-symbols arguments");
@@ -1176,6 +1182,8 @@ class Dex2Oat FINAL {
AssignIfExists(args, M::OutputVdexFd, &output_vdex_fd_);
AssignIfExists(args, M::InputVdex, &input_vdex_);
AssignIfExists(args, M::OutputVdex, &output_vdex_);
+ AssignIfExists(args, M::DmFd, &dm_fd_);
+ AssignIfExists(args, M::DmFile, &dm_file_location_);
AssignIfExists(args, M::OatFd, &oat_fd_);
AssignIfExists(args, M::OatLocation, &oat_location_);
AssignIfExists(args, M::Watchdog, &parser_options->watch_dog_enabled);
@@ -1389,6 +1397,42 @@ class Dex2Oat FINAL {
}
}
+ if (dm_fd_ != -1 || !dm_file_location_.empty()) {
+ std::string error_msg;
+ if (dm_fd_ != -1) {
+ dm_file_.reset(ZipArchive::OpenFromFd(dm_fd_, "DexMetadata", &error_msg));
+ } else {
+ dm_file_.reset(ZipArchive::Open(dm_file_location_.c_str(), &error_msg));
+ }
+ if (dm_file_ == nullptr) {
+ LOG(WARNING) << "Could not open DexMetadata archive " << error_msg;
+ }
+ }
+
+ if (dm_file_ != nullptr) {
+ DCHECK(input_vdex_file_ == nullptr);
+ std::string error_msg;
+ static const char* kDexMetadata = "DexMetadata";
+ std::unique_ptr<ZipEntry> zip_entry(dm_file_->Find(VdexFile::kVdexNameInDmFile, &error_msg));
+ if (zip_entry == nullptr) {
+ LOG(INFO) << "No " << VdexFile::kVdexNameInDmFile << " file in DexMetadata archive. "
+ << "Not doing fast verification.";
+ } else {
+ std::unique_ptr<MemMap> input_file;
+ if (zip_entry->IsUncompressed()) {
+ input_file.reset(zip_entry->MapDirectlyFromFile(VdexFile::kVdexNameInDmFile, &error_msg));
+ } else {
+ input_file.reset(zip_entry->ExtractToMemMap(
+ kDexMetadata, VdexFile::kVdexNameInDmFile, &error_msg));
+ }
+ if (input_file == nullptr) {
+ LOG(WARNING) << "Could not open vdex file in DexMetadata archive: " << error_msg;
+ } else {
+ input_vdex_file_ = std::make_unique<VdexFile>(input_file.release());
+ }
+ }
+ }
+
// Swap file handling
//
// If the swap fd is not -1, we assume this is the file descriptor of an open but unlinked file
@@ -2240,7 +2284,7 @@ class Dex2Oat FINAL {
}
bool DoEagerUnquickeningOfVdex() const {
- return MayInvalidateVdexMetadata();
+ return MayInvalidateVdexMetadata() && dm_file_ == nullptr;
}
bool LoadProfile() {
@@ -2790,6 +2834,9 @@ class Dex2Oat FINAL {
std::string input_vdex_;
std::string output_vdex_;
std::unique_ptr<VdexFile> input_vdex_file_;
+ int dm_fd_;
+ std::string dm_file_location_;
+ std::unique_ptr<ZipArchive> dm_file_;
std::vector<const char*> dex_filenames_;
std::vector<const char*> dex_locations_;
int zip_fd_;
diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc
index a2e2b48956..0eecc84605 100644
--- a/dex2oat/dex2oat_options.cc
+++ b/dex2oat/dex2oat_options.cc
@@ -86,6 +86,12 @@ static void AddGeneratedArtifactMappings(Builder& builder) {
.Define("--output-vdex=_")
.WithType<std::string>()
.IntoKey(M::OutputVdex)
+ .Define("--dm-fd=_")
+ .WithType<int>()
+ .IntoKey(M::DmFd)
+ .Define("--dm-file=_")
+ .WithType<std::string>()
+ .IntoKey(M::DmFile)
.Define("--oat-file=_")
.WithType<std::vector<std::string>>().AppendValues()
.IntoKey(M::OatFiles)
diff --git a/dex2oat/dex2oat_options.def b/dex2oat/dex2oat_options.def
index 9362a3df6f..9a8bdf4aee 100644
--- a/dex2oat/dex2oat_options.def
+++ b/dex2oat/dex2oat_options.def
@@ -43,6 +43,8 @@ DEX2OAT_OPTIONS_KEY (int, InputVdexFd)
DEX2OAT_OPTIONS_KEY (std::string, InputVdex)
DEX2OAT_OPTIONS_KEY (int, OutputVdexFd)
DEX2OAT_OPTIONS_KEY (std::string, OutputVdex)
+DEX2OAT_OPTIONS_KEY (int, DmFd)
+DEX2OAT_OPTIONS_KEY (std::string, DmFile)
DEX2OAT_OPTIONS_KEY (std::vector<std::string>, OatFiles)
DEX2OAT_OPTIONS_KEY (std::vector<std::string>, OatSymbols)
DEX2OAT_OPTIONS_KEY (int, OatFd)
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 6fcf6952e8..5614ac6458 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -783,7 +783,7 @@ class Dex2oatLayoutTest : public Dex2oatTest {
app_image_file_name,
/* use_fd */ true,
/* num_profile_classes */ 1,
- { input_vdex, output_vdex, kDisableCompactDex });
+ { input_vdex, output_vdex });
EXPECT_GT(vdex_file1->GetLength(), 0u);
}
{
@@ -904,7 +904,7 @@ class Dex2oatUnquickenTest : public Dex2oatTest {
GenerateOdexForTest(dex_location,
odex_location,
CompilerFilter::kQuicken,
- { input_vdex, output_vdex, kDisableCompactDex },
+ { input_vdex, output_vdex },
/* expect_success */ true,
/* use_fd */ true);
EXPECT_GT(vdex_file1->GetLength(), 0u);
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index c7e9cdaae0..d245cd1c63 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -3367,7 +3367,10 @@ bool OatWriter::WriteDexFiles(OutputStream* out, File* file, bool update_input_v
// Write shared dex file data section and fix up the dex file headers.
vdex_dex_shared_data_offset_ = vdex_size_;
+ uint32_t shared_data_size = 0u;
+
if (dex_container_ != nullptr) {
+ CHECK(!update_input_vdex) << "Update input vdex should have empty dex container";
DexContainer::Section* const section = dex_container_->GetDataSection();
if (section->Size() > 0) {
const uint32_t shared_data_offset = vdex_size_;
@@ -3376,7 +3379,7 @@ bool OatWriter::WriteDexFiles(OutputStream* out, File* file, bool update_input_v
LOG(ERROR) << "Expected offset " << shared_data_offset << " but got " << existing_offset;
return false;
}
- const uint32_t shared_data_size = section->Size();
+ shared_data_size = section->Size();
if (!out->WriteFully(section->Begin(), shared_data_size)) {
LOG(ERROR) << "Failed to write shared data!";
return false;
@@ -3400,8 +3403,6 @@ bool OatWriter::WriteDexFiles(OutputStream* out, File* file, bool update_input_v
return false;
}
}
- vdex_size_ += shared_data_size;
- size_dex_file_ += shared_data_size;
section->Clear();
if (!out->Flush()) {
PLOG(ERROR) << "Failed to flush after writing shared dex section.";
@@ -3409,9 +3410,35 @@ bool OatWriter::WriteDexFiles(OutputStream* out, File* file, bool update_input_v
}
}
dex_container_.reset();
+ } else {
+ if (update_input_vdex) {
+ for (OatDexFile& oat_dex_file : oat_dex_files_) {
+ DexFile::Header header;
+ if (!file->PreadFully(&header, sizeof(header), oat_dex_file.dex_file_offset_)) {
+ PLOG(ERROR) << "Failed to read dex header";
+ return false;
+ }
+ if (!CompactDexFile::IsMagicValid(header.magic_)) {
+ // Non compact dex does not have shared data section.
+ continue;
+ }
+ const uint32_t expected_data_off = vdex_dex_shared_data_offset_ -
+ oat_dex_file.dex_file_offset_;
+ if (header.data_off_ != expected_data_off) {
+ PLOG(ERROR) << "Shared data section offset " << header.data_off_
+ << " does not match expected value " << expected_data_off;
+ return false;
+ }
+ // The different dex files currently can have different data sizes since
+ // the dex writer writes them one at a time into the shared section.:w
+ shared_data_size = std::max(shared_data_size, header.data_size_);
+ }
+ }
}
+ vdex_size_ += shared_data_size;
+ size_dex_file_ += shared_data_size;
} else {
- vdex_dex_shared_data_offset_ = vdex_size_;
+ vdex_dex_shared_data_offset_ = vdex_size_;
}
return true;
diff --git a/dexdump/Android.bp b/dexdump/Android.bp
index eca08448bc..f6b7a6b68a 100644
--- a/dexdump/Android.bp
+++ b/dexdump/Android.bp
@@ -45,7 +45,6 @@ art_cc_binary {
host_supported: true,
device_supported: false,
static_libs: [
- "libdexfile",
"libbase",
] + art_static_dependencies,
target: {
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 16cb302a84..01b28b55cf 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -47,9 +47,10 @@
#include <sstream>
#include <vector>
+#include "android-base/logging.h"
#include "android-base/stringprintf.h"
-#include "dex/code_item_accessors-no_art-inl.h"
+#include "dex/code_item_accessors-inl.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_file_exception_helpers.h"
#include "dex/dex_file_loader.h"
@@ -1179,7 +1180,7 @@ static void dumpBytecodes(const DexFile* pDexFile, u4 idx,
const Instruction* instruction = &pair.Inst();
const u4 insnWidth = instruction->SizeInCodeUnits();
if (insnWidth == 0) {
- fprintf(stderr, "GLITCH: zero-width instruction at idx=0x%04x\n", pair.DexPc());
+ LOG(WARNING) << "GLITCH: zero-width instruction at idx=0x" << std::hex << pair.DexPc();
break;
}
dumpInstruction(pDexFile, pCode, codeOffset, pair.DexPc(), insnWidth, instruction);
@@ -1259,7 +1260,7 @@ static void dumpMethod(const DexFile* pDexFile, u4 idx, u4 flags,
fprintf(gOutFile, "<method name=\"%s\"\n", name);
const char* returnType = strrchr(typeDescriptor, ')');
if (returnType == nullptr) {
- fprintf(stderr, "bad method type descriptor '%s'\n", typeDescriptor);
+ LOG(ERROR) << "bad method type descriptor '" << typeDescriptor << "'";
goto bail;
}
std::unique_ptr<char[]> dot(descriptorToDot(returnType + 1));
@@ -1278,7 +1279,7 @@ static void dumpMethod(const DexFile* pDexFile, u4 idx, u4 flags,
// Parameters.
if (typeDescriptor[0] != '(') {
- fprintf(stderr, "ERROR: bad descriptor '%s'\n", typeDescriptor);
+ LOG(ERROR) << "ERROR: bad descriptor '" << typeDescriptor << "'";
goto bail;
}
char* tmpBuf = reinterpret_cast<char*>(malloc(strlen(typeDescriptor) + 1));
@@ -1297,7 +1298,7 @@ static void dumpMethod(const DexFile* pDexFile, u4 idx, u4 flags,
} else {
// Primitive char, copy it.
if (strchr("ZBCSIFJD", *base) == nullptr) {
- fprintf(stderr, "ERROR: bad method signature '%s'\n", base);
+ LOG(ERROR) << "ERROR: bad method signature '" << base << "'";
break; // while
}
*cp++ = *base++;
@@ -1444,7 +1445,7 @@ static void dumpClass(const DexFile* pDexFile, int idx, char** pLastPackage) {
if (!(classDescriptor[0] == 'L' &&
classDescriptor[strlen(classDescriptor)-1] == ';')) {
// Arrays and primitives should not be defined explicitly. Keep going?
- fprintf(stderr, "Malformed class name '%s'\n", classDescriptor);
+ LOG(WARNING) << "Malformed class name '" << classDescriptor << "'";
} else if (gOptions.outputFormat == OUTPUT_XML) {
char* mangle = strdup(classDescriptor + 1);
mangle[strlen(mangle)-1] = '\0';
@@ -1694,7 +1695,7 @@ static void dumpCallSite(const DexFile* pDexFile, u4 idx) {
const DexFile::CallSiteIdItem& call_site_id = pDexFile->GetCallSiteId(idx);
CallSiteArrayValueIterator it(*pDexFile, call_site_id);
if (it.Size() < 3) {
- fprintf(stderr, "ERROR: Call site %u has too few values.\n", idx);
+ LOG(ERROR) << "ERROR: Call site " << idx << " has too few values.";
return;
}
@@ -1915,8 +1916,7 @@ int processFile(const char* fileName) {
size_t size = 0;
std::string error_msg;
if (!openAndMapFile(fileName, &base, &size, &error_msg)) {
- fputs(error_msg.c_str(), stderr);
- fputc('\n', stderr);
+ LOG(ERROR) << error_msg;
return -1;
}
const DexFileLoader dex_file_loader;
@@ -1925,8 +1925,7 @@ int processFile(const char* fileName) {
base, size, fileName, /*verify*/ true, kVerifyChecksum, &error_msg, &dex_files)) {
// Display returned error message to user. Note that this error behavior
// differs from the error messages shown by the original Dalvik dexdump.
- fputs(error_msg.c_str(), stderr);
- fputc('\n', stderr);
+ LOG(ERROR) << error_msg;
return -1;
}
diff --git a/dexdump/dexdump_cfg.cc b/dexdump/dexdump_cfg.cc
index 0e313572bc..69ee0682a3 100644
--- a/dexdump/dexdump_cfg.cc
+++ b/dexdump/dexdump_cfg.cc
@@ -25,7 +25,7 @@
#include <set>
#include <sstream>
-#include "dex/code_item_accessors-no_art-inl.h"
+#include "dex/code_item_accessors-inl.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_file_exception_helpers.h"
#include "dex/dex_instruction-inl.h"
diff --git a/dexdump/dexdump_main.cc b/dexdump/dexdump_main.cc
index 2247e7a7e6..3c16fbe008 100644
--- a/dexdump/dexdump_main.cc
+++ b/dexdump/dexdump_main.cc
@@ -28,6 +28,8 @@
#include <string.h>
#include <unistd.h>
+#include <android-base/logging.h>
+
namespace art {
static const char* gProgName = "dexdump";
@@ -36,19 +38,19 @@ static const char* gProgName = "dexdump";
* Shows usage.
*/
static void usage(void) {
- fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
- fprintf(stderr, "%s: [-a] [-c] [-d] [-e] [-f] [-h] [-i] [-l layout] [-o outfile]"
- " dexfile...\n\n", gProgName);
- fprintf(stderr, " -a : display annotations\n");
- fprintf(stderr, " -c : verify checksum and exit\n");
- fprintf(stderr, " -d : disassemble code sections\n");
- fprintf(stderr, " -e : display exported items only\n");
- fprintf(stderr, " -f : display summary information from file header\n");
- fprintf(stderr, " -g : display CFG for dex\n");
- fprintf(stderr, " -h : display file header details\n");
- fprintf(stderr, " -i : ignore checksum failures\n");
- fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n");
- fprintf(stderr, " -o : output file name (defaults to stdout)\n");
+ LOG(ERROR) << "Copyright (C) 2007 The Android Open Source Project\n";
+ LOG(ERROR) << gProgName << ": [-a] [-c] [-d] [-e] [-f] [-h] [-i] [-l layout] [-o outfile]"
+ " dexfile...\n";
+ LOG(ERROR) << " -a : display annotations";
+ LOG(ERROR) << " -c : verify checksum and exit";
+ LOG(ERROR) << " -d : disassemble code sections";
+ LOG(ERROR) << " -e : display exported items only";
+ LOG(ERROR) << " -f : display summary information from file header";
+ LOG(ERROR) << " -g : display CFG for dex";
+ LOG(ERROR) << " -h : display file header details";
+ LOG(ERROR) << " -i : ignore checksum failures";
+ LOG(ERROR) << " -l : output layout, either 'plain' or 'xml'";
+ LOG(ERROR) << " -o : output file name (defaults to stdout)";
}
/*
@@ -112,11 +114,11 @@ int dexdumpDriver(int argc, char** argv) {
// Detect early problems.
if (optind == argc) {
- fprintf(stderr, "%s: no file specified\n", gProgName);
+ LOG(ERROR) << "No file specified";
wantUsage = true;
}
if (gOptions.checksumOnly && gOptions.ignoreBadChecksum) {
- fprintf(stderr, "Can't specify both -c and -i\n");
+ LOG(ERROR) << "Can't specify both -c and -i";
wantUsage = true;
}
if (wantUsage) {
@@ -128,7 +130,7 @@ int dexdumpDriver(int argc, char** argv) {
if (gOptions.outputFileName) {
gOutFile = fopen(gOptions.outputFileName, "w");
if (!gOutFile) {
- fprintf(stderr, "Can't open %s\n", gOptions.outputFileName);
+ PLOG(ERROR) << "Can't open " << gOptions.outputFileName;
return 1;
}
}
@@ -144,5 +146,8 @@ int dexdumpDriver(int argc, char** argv) {
} // namespace art
int main(int argc, char** argv) {
+ // Output all logging to stderr.
+ android::base::SetLogger(android::base::StderrLogger);
+
return art::dexdumpDriver(argc, argv);
}
diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp
index 23ad5fd17e..3ea7f4ba82 100644
--- a/dexlayout/Android.bp
+++ b/dexlayout/Android.bp
@@ -26,7 +26,10 @@ art_cc_defaults {
"dex_writer.cc",
],
export_include_dirs: ["."],
- shared_libs: ["libbase"],
+ shared_libs: [
+ "libdexfile",
+ "libbase",
+ ],
static_libs: ["libz"],
}
@@ -85,6 +88,7 @@ art_cc_binary {
art_cc_test {
name: "art_dexlayout_tests",
defaults: ["art_gtest_defaults"],
+ shared_libs: ["libart-dexlayout"],
srcs: ["dexlayout_test.cc"],
}
diff --git a/dexlayout/compact_dex_writer.cc b/dexlayout/compact_dex_writer.cc
index 6149e75753..08438c4f4a 100644
--- a/dexlayout/compact_dex_writer.cc
+++ b/dexlayout/compact_dex_writer.cc
@@ -121,11 +121,14 @@ CompactDexWriter::ScopedDataSectionItem::~ScopedDataSectionItem() {
const uint32_t deduped_offset = deduper_->Dedupe(start_offset_,
stream_->Tell(),
item_->GetOffset());
- // In case we dedupe to something with wrong alignment, just say we didn't dedupe.
+ // If we deduped, only use the deduped offset if the alignment matches the required alignment.
+ // Otherwise, return without deduping.
if (deduped_offset != Deduper::kDidNotDedupe && IsAlignedParam(deduped_offset, alignment_)) {
+ // Update the IR offset to the offset of the deduped item.
item_->SetOffset(deduped_offset);
+ // Clear the written data for the item so that the stream write doesn't abort in the future.
stream_->Clear(start_offset_, stream_->Tell() - start_offset_);
- // Undo the offset for all that we wrote since we deduped.
+ // Since we deduped, restore the offset to the original position.
stream_->Seek(start_offset_);
}
}
@@ -319,6 +322,7 @@ void CompactDexWriter::WriteStringData(Stream* stream, dex_ir::StringData* strin
}
void CompactDexWriter::Write(DexContainer* output) {
+ CHECK(compute_offsets_);
CHECK(output->IsCompactDexContainer());
Container* const container = down_cast<Container*>(output);
// For now, use the same stream for both data and metadata.
diff --git a/dexlayout/compact_dex_writer.h b/dexlayout/compact_dex_writer.h
index 4834bfc3af..24d0fbf61d 100644
--- a/dexlayout/compact_dex_writer.h
+++ b/dexlayout/compact_dex_writer.h
@@ -60,11 +60,14 @@ class CompactDexWriter : public DexWriter {
return false;
}
const uint8_t* data = Data();
+ DCHECK_LE(a.offset_ + a.length_, section_->Size());
+ DCHECK_LE(b.offset_ + b.length_, section_->Size());
return std::equal(data + a.offset_, data + a.offset_ + a.length_, data + b.offset_);
}
// Hash function.
size_t operator()(const HashedMemoryRange& range) const {
+ DCHECK_LE(range.offset_ + range.length_, section_->Size());
return HashBytes(Data() + range.offset_, range.length_);
}
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index 1a84d2307d..d28b824c7b 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -27,8 +27,8 @@
#include "base/stl_util.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_file_types.h"
+#include "dex/utf.h"
#include "leb128.h"
-#include "utf.h"
namespace art {
namespace dex_ir {
diff --git a/dexlayout/dex_ir_builder.cc b/dexlayout/dex_ir_builder.cc
index 3ec163cea1..4f9bcdd742 100644
--- a/dexlayout/dex_ir_builder.cc
+++ b/dexlayout/dex_ir_builder.cc
@@ -20,13 +20,18 @@
#include <vector>
#include "dex_ir_builder.h"
+#include "dexlayout.h"
namespace art {
namespace dex_ir {
-static void CheckAndSetRemainingOffsets(const DexFile& dex_file, Collections* collections);
+static void CheckAndSetRemainingOffsets(const DexFile& dex_file,
+ Collections* collections,
+ const Options& options);
-Header* DexIrBuilder(const DexFile& dex_file, bool eagerly_assign_offsets) {
+Header* DexIrBuilder(const DexFile& dex_file,
+ bool eagerly_assign_offsets,
+ const Options& options) {
const DexFile::Header& disk_header = dex_file.GetHeader();
Header* header = new Header(disk_header.magic_,
disk_header.checksum_,
@@ -70,13 +75,22 @@ Header* DexIrBuilder(const DexFile& dex_file, bool eagerly_assign_offsets) {
// ClassDef table.
collections.SetClassDefsOffset(disk_header.class_defs_off_);
for (uint32_t i = 0; i < dex_file.NumClassDefs(); ++i) {
+ if (!options.class_filter_.empty()) {
+ // If the filter is enabled (not empty), filter out classes that don't have a matching
+ // descriptor.
+ const DexFile::ClassDef& class_def = dex_file.GetClassDef(i);
+ const char* descriptor = dex_file.GetClassDescriptor(class_def);
+ if (options.class_filter_.find(descriptor) == options.class_filter_.end()) {
+ continue;
+ }
+ }
collections.CreateClassDef(dex_file, i);
}
// MapItem.
collections.SetMapListOffset(disk_header.map_off_);
// CallSiteIds and MethodHandleItems.
collections.CreateCallSitesAndMethodHandles(dex_file);
- CheckAndSetRemainingOffsets(dex_file, &collections);
+ CheckAndSetRemainingOffsets(dex_file, &collections, options);
// Sort the vectors by the map order (same order as the file).
collections.SortVectorsByMapOrder();
@@ -89,7 +103,9 @@ Header* DexIrBuilder(const DexFile& dex_file, bool eagerly_assign_offsets) {
return header;
}
-static void CheckAndSetRemainingOffsets(const DexFile& dex_file, Collections* collections) {
+static void CheckAndSetRemainingOffsets(const DexFile& dex_file,
+ Collections* collections,
+ const Options& options) {
const DexFile::Header& disk_header = dex_file.GetHeader();
// Read MapItems and validate/set remaining offsets.
const DexFile::MapList* map = dex_file.GetMapList();
@@ -122,7 +138,10 @@ static void CheckAndSetRemainingOffsets(const DexFile& dex_file, Collections* co
CHECK_EQ(item->offset_, collections->MethodIdsOffset());
break;
case DexFile::kDexTypeClassDefItem:
- CHECK_EQ(item->size_, collections->ClassDefsSize());
+ if (options.class_filter_.empty()) {
+ // The filter may have removed some classes, this will get fixed up during writing.
+ CHECK_EQ(item->size_, collections->ClassDefsSize());
+ }
CHECK_EQ(item->offset_, collections->ClassDefsOffset());
break;
case DexFile::kDexTypeCallSiteIdItem:
diff --git a/dexlayout/dex_ir_builder.h b/dexlayout/dex_ir_builder.h
index 4d4b4e8699..9f5377fe56 100644
--- a/dexlayout/dex_ir_builder.h
+++ b/dexlayout/dex_ir_builder.h
@@ -22,11 +22,16 @@
#include "dex_ir.h"
namespace art {
+
+class Options;
+
namespace dex_ir {
-// Eagerly assign offsets assigns offsets based on the original offsets in the input dex file. If
-// this not done, dex_ir::Item::GetOffset will abort when reading uninitialized offsets.
-dex_ir::Header* DexIrBuilder(const DexFile& dex_file, bool eagerly_assign_offsets);
+// Eagerly assign offsets based on the original offsets in the input dex file. If this is not done,
+// dex_ir::Item::GetOffset will abort when reading uninitialized offsets.
+dex_ir::Header* DexIrBuilder(const DexFile& dex_file,
+ bool eagerly_assign_offsets,
+ const Options& options);
} // namespace dex_ir
} // namespace art
diff --git a/dexlayout/dex_visualize.cc b/dexlayout/dex_visualize.cc
index e4ed69b8d2..516a3382fd 100644
--- a/dexlayout/dex_visualize.cc
+++ b/dexlayout/dex_visualize.cc
@@ -29,6 +29,8 @@
#include <memory>
#include <vector>
+#include <android-base/logging.h>
+
#include "dex_ir.h"
#include "dexlayout.h"
#include "jit/profile_compilation_info.h"
@@ -246,7 +248,7 @@ void VisualizeDexLayout(dex_ir::Header* header,
ProfileCompilationInfo* profile_info) {
std::unique_ptr<Dumper> dumper(new Dumper(header));
if (!dumper->OpenAndPrintHeader(dex_file_index)) {
- fprintf(stderr, "Could not open output file.\n");
+ LOG(ERROR) << "Could not open output file.";
return;
}
diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc
index a9aa97dc80..808bfad029 100644
--- a/dexlayout/dex_writer.cc
+++ b/dexlayout/dex_writer.cc
@@ -25,8 +25,8 @@
#include "dex/dex_file_layout.h"
#include "dex/dex_file_types.h"
#include "dex/standard_dex_file.h"
+#include "dex/utf.h"
#include "dexlayout.h"
-#include "utf.h"
namespace art {
@@ -114,8 +114,7 @@ DexWriter::DexWriter(DexLayout* dex_layout, bool compute_offsets)
dex_layout_(dex_layout),
compute_offsets_(compute_offsets) {}
-size_t DexWriter::WriteEncodedValue(Stream* stream, dex_ir::EncodedValue* encoded_value) {
- size_t original_offset = stream->Tell();
+void DexWriter::WriteEncodedValue(Stream* stream, dex_ir::EncodedValue* encoded_value) {
size_t start = 0;
size_t length;
uint8_t buffer[8];
@@ -166,39 +165,37 @@ size_t DexWriter::WriteEncodedValue(Stream* stream, dex_ir::EncodedValue* encode
case DexFile::kDexAnnotationArray:
WriteEncodedValueHeader(stream, type, 0);
WriteEncodedArray(stream, encoded_value->GetEncodedArray()->GetEncodedValues());
- return stream->Tell() - original_offset;
+ return;
case DexFile::kDexAnnotationAnnotation:
WriteEncodedValueHeader(stream, type, 0);
WriteEncodedAnnotation(stream, encoded_value->GetEncodedAnnotation());
- return stream->Tell() - original_offset;
+ return;
case DexFile::kDexAnnotationNull:
- return WriteEncodedValueHeader(stream, type, 0);
+ WriteEncodedValueHeader(stream, type, 0);
+ return;
case DexFile::kDexAnnotationBoolean:
- return WriteEncodedValueHeader(stream, type, encoded_value->GetBoolean() ? 1 : 0);
+ WriteEncodedValueHeader(stream, type, encoded_value->GetBoolean() ? 1 : 0);
+ return;
default:
- return 0;
+ return;
}
WriteEncodedValueHeader(stream, type, length - 1);
stream->Write(buffer + start, length);
- return stream->Tell() - original_offset;
}
-size_t DexWriter::WriteEncodedValueHeader(Stream* stream, int8_t value_type, size_t value_arg) {
+void DexWriter::WriteEncodedValueHeader(Stream* stream, int8_t value_type, size_t value_arg) {
uint8_t buffer[1] = { static_cast<uint8_t>((value_arg << 5) | value_type) };
- return stream->Write(buffer, sizeof(uint8_t));
+ stream->Write(buffer, sizeof(uint8_t));
}
-size_t DexWriter::WriteEncodedArray(Stream* stream, dex_ir::EncodedValueVector* values) {
- size_t original_offset = stream->Tell();
+void DexWriter::WriteEncodedArray(Stream* stream, dex_ir::EncodedValueVector* values) {
stream->WriteUleb128(values->size());
for (std::unique_ptr<dex_ir::EncodedValue>& value : *values) {
WriteEncodedValue(stream, value.get());
}
- return stream->Tell() - original_offset;
}
-size_t DexWriter::WriteEncodedAnnotation(Stream* stream, dex_ir::EncodedAnnotation* annotation) {
- size_t original_offset = stream->Tell();
+void DexWriter::WriteEncodedAnnotation(Stream* stream, dex_ir::EncodedAnnotation* annotation) {
stream->WriteUleb128(annotation->GetType()->GetIndex());
stream->WriteUleb128(annotation->GetAnnotationElements()->size());
for (std::unique_ptr<dex_ir::AnnotationElement>& annotation_element :
@@ -206,11 +203,9 @@ size_t DexWriter::WriteEncodedAnnotation(Stream* stream, dex_ir::EncodedAnnotati
stream->WriteUleb128(annotation_element->GetName()->GetIndex());
WriteEncodedValue(stream, annotation_element->GetValue());
}
- return stream->Tell() - original_offset;
}
-size_t DexWriter::WriteEncodedFields(Stream* stream, dex_ir::FieldItemVector* fields) {
- size_t original_offset = stream->Tell();
+void DexWriter::WriteEncodedFields(Stream* stream, dex_ir::FieldItemVector* fields) {
uint32_t prev_index = 0;
for (std::unique_ptr<dex_ir::FieldItem>& field : *fields) {
uint32_t index = field->GetFieldId()->GetIndex();
@@ -218,11 +213,9 @@ size_t DexWriter::WriteEncodedFields(Stream* stream, dex_ir::FieldItemVector* fi
stream->WriteUleb128(field->GetAccessFlags());
prev_index = index;
}
- return stream->Tell() - original_offset;
}
-size_t DexWriter::WriteEncodedMethods(Stream* stream, dex_ir::MethodItemVector* methods) {
- size_t original_offset = stream->Tell();
+void DexWriter::WriteEncodedMethods(Stream* stream, dex_ir::MethodItemVector* methods) {
uint32_t prev_index = 0;
for (std::unique_ptr<dex_ir::MethodItem>& method : *methods) {
uint32_t index = method->GetMethodId()->GetIndex();
@@ -232,12 +225,11 @@ size_t DexWriter::WriteEncodedMethods(Stream* stream, dex_ir::MethodItemVector*
stream->WriteUleb128(code_off);
prev_index = index;
}
- return stream->Tell() - original_offset;
}
// TODO: Refactor this to remove duplicated boiler plate. One way to do this is adding
// function that takes a CollectionVector<T> and uses overloading.
-uint32_t DexWriter::WriteStringIds(Stream* stream, bool reserve_only) {
+void DexWriter::WriteStringIds(Stream* stream, bool reserve_only) {
const uint32_t start = stream->Tell();
for (std::unique_ptr<dex_ir::StringId>& string_id : header_->GetCollections().StringIds()) {
stream->AlignTo(SectionAlignment(DexFile::kDexTypeStringIdItem));
@@ -251,7 +243,6 @@ uint32_t DexWriter::WriteStringIds(Stream* stream, bool reserve_only) {
if (compute_offsets_ && start != stream->Tell()) {
header_->GetCollections().SetStringIdsOffset(start);
}
- return stream->Tell() - start;
}
void DexWriter::WriteStringData(Stream* stream, dex_ir::StringData* string_data) {
@@ -263,7 +254,7 @@ void DexWriter::WriteStringData(Stream* stream, dex_ir::StringData* string_data)
stream->Skip(1);
}
-uint32_t DexWriter::WriteStringDatas(Stream* stream) {
+void DexWriter::WriteStringDatas(Stream* stream) {
const uint32_t start = stream->Tell();
for (std::unique_ptr<dex_ir::StringData>& string_data : header_->GetCollections().StringDatas()) {
WriteStringData(stream, string_data.get());
@@ -271,10 +262,9 @@ uint32_t DexWriter::WriteStringDatas(Stream* stream) {
if (compute_offsets_ && start != stream->Tell()) {
header_->GetCollections().SetStringDatasOffset(start);
}
- return stream->Tell() - start;
}
-uint32_t DexWriter::WriteTypeIds(Stream* stream) {
+void DexWriter::WriteTypeIds(Stream* stream) {
uint32_t descriptor_idx[1];
const uint32_t start = stream->Tell();
for (std::unique_ptr<dex_ir::TypeId>& type_id : header_->GetCollections().TypeIds()) {
@@ -286,10 +276,9 @@ uint32_t DexWriter::WriteTypeIds(Stream* stream) {
if (compute_offsets_ && start != stream->Tell()) {
header_->GetCollections().SetTypeIdsOffset(start);
}
- return stream->Tell() - start;
}
-uint32_t DexWriter::WriteTypeLists(Stream* stream) {
+void DexWriter::WriteTypeLists(Stream* stream) {
uint32_t size[1];
uint16_t list[1];
const uint32_t start = stream->Tell();
@@ -306,10 +295,9 @@ uint32_t DexWriter::WriteTypeLists(Stream* stream) {
if (compute_offsets_ && start != stream->Tell()) {
header_->GetCollections().SetTypeListsOffset(start);
}
- return stream->Tell() - start;
}
-uint32_t DexWriter::WriteProtoIds(Stream* stream, bool reserve_only) {
+void DexWriter::WriteProtoIds(Stream* stream, bool reserve_only) {
uint32_t buffer[3];
const uint32_t start = stream->Tell();
for (std::unique_ptr<dex_ir::ProtoId>& proto_id : header_->GetCollections().ProtoIds()) {
@@ -327,10 +315,9 @@ uint32_t DexWriter::WriteProtoIds(Stream* stream, bool reserve_only) {
if (compute_offsets_ && start != stream->Tell()) {
header_->GetCollections().SetProtoIdsOffset(start);
}
- return stream->Tell() - start;
}
-uint32_t DexWriter::WriteFieldIds(Stream* stream) {
+void DexWriter::WriteFieldIds(Stream* stream) {
uint16_t buffer[4];
const uint32_t start = stream->Tell();
for (std::unique_ptr<dex_ir::FieldId>& field_id : header_->GetCollections().FieldIds()) {
@@ -345,10 +332,9 @@ uint32_t DexWriter::WriteFieldIds(Stream* stream) {
if (compute_offsets_ && start != stream->Tell()) {
header_->GetCollections().SetFieldIdsOffset(start);
}
- return stream->Tell() - start;
}
-uint32_t DexWriter::WriteMethodIds(Stream* stream) {
+void DexWriter::WriteMethodIds(Stream* stream) {
uint16_t buffer[4];
const uint32_t start = stream->Tell();
for (std::unique_ptr<dex_ir::MethodId>& method_id : header_->GetCollections().MethodIds()) {
@@ -363,10 +349,9 @@ uint32_t DexWriter::WriteMethodIds(Stream* stream) {
if (compute_offsets_ && start != stream->Tell()) {
header_->GetCollections().SetMethodIdsOffset(start);
}
- return stream->Tell() - start;
}
-uint32_t DexWriter::WriteEncodedArrays(Stream* stream) {
+void DexWriter::WriteEncodedArrays(Stream* stream) {
const uint32_t start = stream->Tell();
for (std::unique_ptr<dex_ir::EncodedArrayItem>& encoded_array :
header_->GetCollections().EncodedArrayItems()) {
@@ -377,10 +362,9 @@ uint32_t DexWriter::WriteEncodedArrays(Stream* stream) {
if (compute_offsets_ && start != stream->Tell()) {
header_->GetCollections().SetEncodedArrayItemsOffset(start);
}
- return stream->Tell() - start;
}
-uint32_t DexWriter::WriteAnnotations(Stream* stream) {
+void DexWriter::WriteAnnotations(Stream* stream) {
uint8_t visibility[1];
const uint32_t start = stream->Tell();
for (std::unique_ptr<dex_ir::AnnotationItem>& annotation :
@@ -394,10 +378,9 @@ uint32_t DexWriter::WriteAnnotations(Stream* stream) {
if (compute_offsets_ && start != stream->Tell()) {
header_->GetCollections().SetAnnotationItemsOffset(start);
}
- return stream->Tell() - start;
}
-uint32_t DexWriter::WriteAnnotationSets(Stream* stream) {
+void DexWriter::WriteAnnotationSets(Stream* stream) {
uint32_t size[1];
uint32_t annotation_off[1];
const uint32_t start = stream->Tell();
@@ -415,10 +398,9 @@ uint32_t DexWriter::WriteAnnotationSets(Stream* stream) {
if (compute_offsets_ && start != stream->Tell()) {
header_->GetCollections().SetAnnotationSetItemsOffset(start);
}
- return stream->Tell() - start;
}
-uint32_t DexWriter::WriteAnnotationSetRefs(Stream* stream) {
+void DexWriter::WriteAnnotationSetRefs(Stream* stream) {
uint32_t size[1];
uint32_t annotations_off[1];
const uint32_t start = stream->Tell();
@@ -436,10 +418,9 @@ uint32_t DexWriter::WriteAnnotationSetRefs(Stream* stream) {
if (compute_offsets_ && start != stream->Tell()) {
header_->GetCollections().SetAnnotationSetRefListsOffset(start);
}
- return stream->Tell() - start;
}
-uint32_t DexWriter::WriteAnnotationsDirectories(Stream* stream) {
+void DexWriter::WriteAnnotationsDirectories(Stream* stream) {
uint32_t directory_buffer[4];
uint32_t annotation_buffer[2];
const uint32_t start = stream->Tell();
@@ -484,7 +465,6 @@ uint32_t DexWriter::WriteAnnotationsDirectories(Stream* stream) {
if (compute_offsets_ && start != stream->Tell()) {
header_->GetCollections().SetAnnotationsDirectoryItemsOffset(start);
}
- return stream->Tell() - start;
}
void DexWriter::WriteDebugInfoItem(Stream* stream, dex_ir::DebugInfoItem* debug_info) {
@@ -493,7 +473,7 @@ void DexWriter::WriteDebugInfoItem(Stream* stream, dex_ir::DebugInfoItem* debug_
stream->Write(debug_info->GetDebugInfo(), debug_info->GetDebugInfoSize());
}
-uint32_t DexWriter::WriteDebugInfoItems(Stream* stream) {
+void DexWriter::WriteDebugInfoItems(Stream* stream) {
const uint32_t start = stream->Tell();
for (std::unique_ptr<dex_ir::DebugInfoItem>& debug_info :
header_->GetCollections().DebugInfoItems()) {
@@ -502,13 +482,11 @@ uint32_t DexWriter::WriteDebugInfoItems(Stream* stream) {
if (compute_offsets_ && start != stream->Tell()) {
header_->GetCollections().SetDebugInfoItemsOffset(start);
}
- return stream->Tell() - start;
}
-uint32_t DexWriter::WriteCodeItemPostInstructionData(Stream* stream,
- dex_ir::CodeItem* code_item,
- bool reserve_only) {
- const uint32_t start_offset = stream->Tell();
+void DexWriter::WriteCodeItemPostInstructionData(Stream* stream,
+ dex_ir::CodeItem* code_item,
+ bool reserve_only) {
if (code_item->TriesSize() != 0) {
stream->AlignTo(DexFile::TryItem::kAlignment);
// Write try items.
@@ -540,7 +518,6 @@ uint32_t DexWriter::WriteCodeItemPostInstructionData(Stream* stream,
}
stream->Seek(max_offset);
}
- return stream->Tell() - start_offset;
}
void DexWriter::WriteCodeItem(Stream* stream,
@@ -574,7 +551,7 @@ void DexWriter::WriteCodeItem(Stream* stream,
}
}
-uint32_t DexWriter::WriteCodeItems(Stream* stream, bool reserve_only) {
+void DexWriter::WriteCodeItems(Stream* stream, bool reserve_only) {
DexLayoutSection* code_section = nullptr;
if (!reserve_only && dex_layout_ != nullptr) {
code_section = &dex_layout_->GetSections().sections_[static_cast<size_t>(
@@ -598,10 +575,9 @@ uint32_t DexWriter::WriteCodeItems(Stream* stream, bool reserve_only) {
if (compute_offsets_ && start != stream->Tell()) {
header_->GetCollections().SetCodeItemsOffset(start);
}
- return stream->Tell() - start;
}
-uint32_t DexWriter::WriteClassDefs(Stream* stream, bool reserve_only) {
+void DexWriter::WriteClassDefs(Stream* stream, bool reserve_only) {
const uint32_t start = stream->Tell();
uint32_t class_def_buffer[8];
for (std::unique_ptr<dex_ir::ClassDef>& class_def : header_->GetCollections().ClassDefs()) {
@@ -628,10 +604,9 @@ uint32_t DexWriter::WriteClassDefs(Stream* stream, bool reserve_only) {
if (compute_offsets_ && start != stream->Tell()) {
header_->GetCollections().SetClassDefsOffset(start);
}
- return stream->Tell() - start;
}
-uint32_t DexWriter::WriteClassDatas(Stream* stream) {
+void DexWriter::WriteClassDatas(Stream* stream) {
const uint32_t start = stream->Tell();
for (const std::unique_ptr<dex_ir::ClassData>& class_data :
header_->GetCollections().ClassDatas()) {
@@ -649,10 +624,9 @@ uint32_t DexWriter::WriteClassDatas(Stream* stream) {
if (compute_offsets_ && start != stream->Tell()) {
header_->GetCollections().SetClassDatasOffset(start);
}
- return stream->Tell() - start;
}
-uint32_t DexWriter::WriteCallSiteIds(Stream* stream, bool reserve_only) {
+void DexWriter::WriteCallSiteIds(Stream* stream, bool reserve_only) {
const uint32_t start = stream->Tell();
uint32_t call_site_off[1];
for (std::unique_ptr<dex_ir::CallSiteId>& call_site_id :
@@ -668,10 +642,9 @@ uint32_t DexWriter::WriteCallSiteIds(Stream* stream, bool reserve_only) {
if (compute_offsets_ && start != stream->Tell()) {
header_->GetCollections().SetCallSiteIdsOffset(start);
}
- return stream->Tell() - start;
}
-uint32_t DexWriter::WriteMethodHandles(Stream* stream) {
+void DexWriter::WriteMethodHandles(Stream* stream) {
const uint32_t start = stream->Tell();
uint16_t method_handle_buff[4];
for (std::unique_ptr<dex_ir::MethodHandleItem>& method_handle :
@@ -686,30 +659,25 @@ uint32_t DexWriter::WriteMethodHandles(Stream* stream) {
if (compute_offsets_ && start != stream->Tell()) {
header_->GetCollections().SetMethodHandleItemsOffset(start);
}
- return stream->Tell() - start;
}
-uint32_t DexWriter::WriteMapItems(Stream* stream, MapItemQueue* queue) {
+void DexWriter::WriteMapItems(Stream* stream, MapItemQueue* queue) {
// All the sections should already have been added.
- uint16_t uint16_buffer[2];
- uint32_t uint32_buffer[2];
- uint16_buffer[1] = 0;
- uint32_buffer[0] = queue->size();
- const uint32_t start = stream->Tell();
- stream->Write(uint32_buffer, sizeof(uint32_t));
+ const uint32_t map_list_size = queue->size();
+ stream->Write(&map_list_size, sizeof(map_list_size));
while (!queue->empty()) {
- const MapItem& map_item = queue->top();
- uint16_buffer[0] = map_item.type_;
- uint32_buffer[0] = map_item.size_;
- uint32_buffer[1] = map_item.offset_;
- stream->Write(uint16_buffer, 2 * sizeof(uint16_t));
- stream->Write(uint32_buffer, 2 * sizeof(uint32_t));
+ const MapItem& item = queue->top();
+ DexFile::MapItem map_item;
+ map_item.type_ = item.type_;
+ map_item.size_ = item.size_;
+ map_item.offset_ = item.offset_;
+ map_item.unused_ = 0u;
+ stream->Write(&map_item, sizeof(map_item));
queue->pop();
}
- return stream->Tell() - start;
}
-uint32_t DexWriter::GenerateAndWriteMapItems(Stream* stream) {
+void DexWriter::GenerateAndWriteMapItems(Stream* stream) {
dex_ir::Collections& collection = header_->GetCollections();
MapItemQueue queue;
@@ -771,9 +739,7 @@ uint32_t DexWriter::GenerateAndWriteMapItems(Stream* stream) {
queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationsDirectoryItem,
collection.AnnotationsDirectoryItemsSize(),
collection.AnnotationsDirectoryItemsOffset()));
-
- // Write the map items.
- return WriteMapItems(stream, &queue);
+ WriteMapItems(stream, &queue);
}
void DexWriter::WriteHeader(Stream* stream) {
diff --git a/dexlayout/dex_writer.h b/dexlayout/dex_writer.h
index e6e05334e4..5df11116ee 100644
--- a/dexlayout/dex_writer.h
+++ b/dexlayout/dex_writer.h
@@ -85,6 +85,7 @@ class DexWriter {
void Seek(size_t position) {
position_ = position;
+ EnsureStorage(0u);
}
// Does not allow overwriting for bug prevention purposes.
@@ -129,10 +130,12 @@ class DexWriter {
ALWAYS_INLINE void AlignTo(const size_t alignment) {
position_ = RoundUp(position_, alignment);
+ EnsureStorage(0u);
}
ALWAYS_INLINE void Skip(const size_t count) {
position_ += count;
+ EnsureStorage(0u);
}
class ScopedSeek {
@@ -221,43 +224,43 @@ class DexWriter {
virtual void Write(DexContainer* output);
virtual std::unique_ptr<DexContainer> CreateDexContainer() const;
- size_t WriteEncodedValue(Stream* stream, dex_ir::EncodedValue* encoded_value);
- size_t WriteEncodedValueHeader(Stream* stream, int8_t value_type, size_t value_arg);
- size_t WriteEncodedArray(Stream* stream, dex_ir::EncodedValueVector* values);
- size_t WriteEncodedAnnotation(Stream* stream, dex_ir::EncodedAnnotation* annotation);
- size_t WriteEncodedFields(Stream* stream, dex_ir::FieldItemVector* fields);
- size_t WriteEncodedMethods(Stream* stream, dex_ir::MethodItemVector* methods);
+ void WriteEncodedValue(Stream* stream, dex_ir::EncodedValue* encoded_value);
+ void WriteEncodedValueHeader(Stream* stream, int8_t value_type, size_t value_arg);
+ void WriteEncodedArray(Stream* stream, dex_ir::EncodedValueVector* values);
+ void WriteEncodedAnnotation(Stream* stream, dex_ir::EncodedAnnotation* annotation);
+ void WriteEncodedFields(Stream* stream, dex_ir::FieldItemVector* fields);
+ void WriteEncodedMethods(Stream* stream, dex_ir::MethodItemVector* methods);
// Header and id section
virtual void WriteHeader(Stream* stream);
virtual size_t GetHeaderSize() const;
// reserve_only means don't write, only reserve space. This is required since the string data
// offsets must be assigned.
- uint32_t WriteStringIds(Stream* stream, bool reserve_only);
- uint32_t WriteTypeIds(Stream* stream);
- uint32_t WriteProtoIds(Stream* stream, bool reserve_only);
- uint32_t WriteFieldIds(Stream* stream);
- uint32_t WriteMethodIds(Stream* stream);
- uint32_t WriteClassDefs(Stream* stream, bool reserve_only);
- uint32_t WriteCallSiteIds(Stream* stream, bool reserve_only);
-
- uint32_t WriteEncodedArrays(Stream* stream);
- uint32_t WriteAnnotations(Stream* stream);
- uint32_t WriteAnnotationSets(Stream* stream);
- uint32_t WriteAnnotationSetRefs(Stream* stream);
- uint32_t WriteAnnotationsDirectories(Stream* stream);
+ void WriteStringIds(Stream* stream, bool reserve_only);
+ void WriteTypeIds(Stream* stream);
+ void WriteProtoIds(Stream* stream, bool reserve_only);
+ void WriteFieldIds(Stream* stream);
+ void WriteMethodIds(Stream* stream);
+ void WriteClassDefs(Stream* stream, bool reserve_only);
+ void WriteCallSiteIds(Stream* stream, bool reserve_only);
+
+ void WriteEncodedArrays(Stream* stream);
+ void WriteAnnotations(Stream* stream);
+ void WriteAnnotationSets(Stream* stream);
+ void WriteAnnotationSetRefs(Stream* stream);
+ void WriteAnnotationsDirectories(Stream* stream);
// Data section.
- uint32_t WriteDebugInfoItems(Stream* stream);
- uint32_t WriteCodeItems(Stream* stream, bool reserve_only);
- uint32_t WriteTypeLists(Stream* stream);
- uint32_t WriteStringDatas(Stream* stream);
- uint32_t WriteClassDatas(Stream* stream);
- uint32_t WriteMethodHandles(Stream* stream);
- uint32_t WriteMapItems(Stream* stream, MapItemQueue* queue);
- uint32_t GenerateAndWriteMapItems(Stream* stream);
-
- virtual uint32_t WriteCodeItemPostInstructionData(Stream* stream,
+ void WriteDebugInfoItems(Stream* stream);
+ void WriteCodeItems(Stream* stream, bool reserve_only);
+ void WriteTypeLists(Stream* stream);
+ void WriteStringDatas(Stream* stream);
+ void WriteClassDatas(Stream* stream);
+ void WriteMethodHandles(Stream* stream);
+ void WriteMapItems(Stream* stream, MapItemQueue* queue);
+ void GenerateAndWriteMapItems(Stream* stream);
+
+ virtual void WriteCodeItemPostInstructionData(Stream* stream,
dex_ir::CodeItem* item,
bool reserve_only);
virtual void WriteCodeItem(Stream* stream, dex_ir::CodeItem* item, bool reserve_only);
diff --git a/dexlayout/dexdiag.cc b/dexlayout/dexdiag.cc
index 99b1f38f73..c0d6f02c00 100644
--- a/dexlayout/dexdiag.cc
+++ b/dexlayout/dexdiag.cc
@@ -29,6 +29,7 @@
#include "base/logging.h" // For InitLogging.
#include "base/stringpiece.h"
+#include "dexlayout.h"
#include "dex/dex_file.h"
#include "dex_ir.h"
#include "dex_ir_builder.h"
@@ -290,8 +291,10 @@ static void ProcessOneDexMapping(uint64_t* pagemap,
// Build a list of the dex file section types, sorted from highest offset to lowest.
std::vector<dex_ir::DexFileSection> sections;
{
+ Options options;
std::unique_ptr<dex_ir::Header> header(dex_ir::DexIrBuilder(*dex_file,
- /*eagerly_assign_offsets*/ true));
+ /*eagerly_assign_offsets*/ true,
+ options));
sections = dex_ir::GetSortedDexFileSections(header.get(),
dex_ir::SortDirection::kSortDescending);
}
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index 1b32f7b0d9..91d35ff6d7 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -1052,7 +1052,7 @@ void DexLayout::DumpBytecodes(uint32_t idx, const dex_ir::CodeItem* code, uint32
for (const DexInstructionPcPair& inst : code->Instructions()) {
const uint32_t insn_width = inst->SizeInCodeUnits();
if (insn_width == 0) {
- fprintf(stderr, "GLITCH: zero-width instruction at idx=0x%04x\n", inst.DexPc());
+ LOG(WARNING) << "GLITCH: zero-width instruction at idx=0x" << std::hex << inst.DexPc();
break;
}
DumpInstruction(code, code_offset, inst.DexPc(), insn_width, &inst.Inst());
@@ -1220,7 +1220,7 @@ void DexLayout::DumpMethod(uint32_t idx, uint32_t flags, const dex_ir::CodeItem*
fprintf(out_file_, "<method name=\"%s\"\n", name);
const char* return_type = strrchr(type_descriptor, ')');
if (return_type == nullptr) {
- fprintf(stderr, "bad method type descriptor '%s'\n", type_descriptor);
+ LOG(ERROR) << "bad method type descriptor '" << type_descriptor << "'";
goto bail;
}
std::string dot(DescriptorToDotWrapper(return_type + 1));
@@ -1239,7 +1239,7 @@ void DexLayout::DumpMethod(uint32_t idx, uint32_t flags, const dex_ir::CodeItem*
// Parameters.
if (type_descriptor[0] != '(') {
- fprintf(stderr, "ERROR: bad descriptor '%s'\n", type_descriptor);
+ LOG(ERROR) << "ERROR: bad descriptor '" << type_descriptor << "'";
goto bail;
}
char* tmp_buf = reinterpret_cast<char*>(malloc(strlen(type_descriptor) + 1));
@@ -1258,7 +1258,7 @@ void DexLayout::DumpMethod(uint32_t idx, uint32_t flags, const dex_ir::CodeItem*
} else {
// Primitive char, copy it.
if (strchr("ZBCSIFJD", *base) == nullptr) {
- fprintf(stderr, "ERROR: bad method signature '%s'\n", base);
+ LOG(ERROR) << "ERROR: bad method signature '" << base << "'";
break; // while
}
*cp++ = *base++;
@@ -1368,7 +1368,7 @@ void DexLayout::DumpClass(int idx, char** last_package) {
if (!(class_descriptor[0] == 'L' &&
class_descriptor[strlen(class_descriptor)-1] == ';')) {
// Arrays and primitives should not be defined explicitly. Keep going?
- fprintf(stderr, "Malformed class name '%s'\n", class_descriptor);
+ LOG(ERROR) << "Malformed class name '" << class_descriptor << "'";
} else if (options_.output_format_ == kOutputXml) {
char* mangle = strdup(class_descriptor + 1);
mangle[strlen(mangle)-1] = '\0';
@@ -1873,7 +1873,9 @@ void DexLayout::ProcessDexFile(const char* file_name,
// These options required the offsets for dumping purposes.
eagerly_assign_offsets = true;
}
- std::unique_ptr<dex_ir::Header> header(dex_ir::DexIrBuilder(*dex_file, eagerly_assign_offsets));
+ std::unique_ptr<dex_ir::Header> header(dex_ir::DexIrBuilder(*dex_file,
+ eagerly_assign_offsets,
+ GetOptions()));
SetHeader(header.get());
if (options_.verbose_) {
@@ -1948,10 +1950,12 @@ void DexLayout::ProcessDexFile(const char* file_name,
// Regenerate output IR to catch any bugs that might happen during writing.
std::unique_ptr<dex_ir::Header> output_header(
dex_ir::DexIrBuilder(*output_dex_file,
- /*eagerly_assign_offsets*/ true));
+ /*eagerly_assign_offsets*/ true,
+ GetOptions()));
std::unique_ptr<dex_ir::Header> orig_header(
dex_ir::DexIrBuilder(*dex_file,
- /*eagerly_assign_offsets*/ true));
+ /*eagerly_assign_offsets*/ true,
+ GetOptions()));
CHECK(VerifyOutputDexFile(output_header.get(), orig_header.get(), &error_msg)) << error_msg;
}
}
@@ -1975,8 +1979,7 @@ int DexLayout::ProcessFile(const char* file_name) {
file_name, file_name, /* verify */ true, verify_checksum, &error_msg, &dex_files)) {
// Display returned error message to user. Note that this error behavior
// differs from the error messages shown by the original Dalvik dexdump.
- fputs(error_msg.c_str(), stderr);
- fputc('\n', stderr);
+ LOG(ERROR) << error_msg;
return -1;
}
diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h
index 00d24dbda4..d2f9cb9ce5 100644
--- a/dexlayout/dexlayout.h
+++ b/dexlayout/dexlayout.h
@@ -72,6 +72,9 @@ class Options {
const char* output_dex_directory_ = nullptr;
const char* output_file_name_ = nullptr;
const char* profile_file_name_ = nullptr;
+ // Filter that removes classes that don't have a matching descriptor (during IR creation).
+ // This speeds up cases when the output only requires a single class.
+ std::set<std::string> class_filter_;
};
// Hotness info
diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc
index ece0f932ce..f30cfee4ec 100644
--- a/dexlayout/dexlayout_main.cc
+++ b/dexlayout/dexlayout_main.cc
@@ -44,25 +44,26 @@ static const char* kProgramName = "dexlayout";
* Shows usage.
*/
static void Usage(void) {
- fprintf(stderr, "Copyright (C) 2016 The Android Open Source Project\n\n");
- fprintf(stderr, "%s: [-a] [-c] [-d] [-e] [-f] [-h] [-i] [-l layout] [-o outfile] [-p profile]"
- " [-s] [-t] [-v] [-w directory] dexfile...\n\n", kProgramName);
- fprintf(stderr, " -a : display annotations\n");
- fprintf(stderr, " -b : build dex_ir\n");
- fprintf(stderr, " -c : verify checksum and exit\n");
- fprintf(stderr, " -d : disassemble code sections\n");
- fprintf(stderr, " -e : display exported items only\n");
- fprintf(stderr, " -f : display summary information from file header\n");
- fprintf(stderr, " -h : display file header details\n");
- fprintf(stderr, " -i : ignore checksum failures\n");
- fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n");
- fprintf(stderr, " -o : output file name (defaults to stdout)\n");
- fprintf(stderr, " -p : profile file name (defaults to no profile)\n");
- fprintf(stderr, " -s : visualize reference pattern\n");
- fprintf(stderr, " -t : display file section sizes\n");
- fprintf(stderr, " -v : verify output file is canonical to input (IR level comparison)\n");
- fprintf(stderr, " -w : output dex directory \n");
- fprintf(stderr, " -x : compact dex generation level, either 'none' or 'fast'\n");
+ LOG(ERROR) << "Copyright (C) 2016 The Android Open Source Project\n";
+ LOG(ERROR) << kProgramName
+ << ": [-a] [-c] [-d] [-e] [-f] [-h] [-i] [-l layout] [-o outfile] [-p profile]"
+ " [-s] [-t] [-v] [-w directory] dexfile...\n";
+ LOG(ERROR) << " -a : display annotations";
+ LOG(ERROR) << " -b : build dex_ir";
+ LOG(ERROR) << " -c : verify checksum and exit";
+ LOG(ERROR) << " -d : disassemble code sections";
+ LOG(ERROR) << " -e : display exported items only";
+ LOG(ERROR) << " -f : display summary information from file header";
+ LOG(ERROR) << " -h : display file header details";
+ LOG(ERROR) << " -i : ignore checksum failures";
+ LOG(ERROR) << " -l : output layout, either 'plain' or 'xml'";
+ LOG(ERROR) << " -o : output file name (defaults to stdout)";
+ LOG(ERROR) << " -p : profile file name (defaults to no profile)";
+ LOG(ERROR) << " -s : visualize reference pattern";
+ LOG(ERROR) << " -t : display file section sizes";
+ LOG(ERROR) << " -v : verify output file is canonical to input (IR level comparison)";
+ LOG(ERROR) << " -w : output dex directory";
+ LOG(ERROR) << " -x : compact dex generation level, either 'none' or 'fast'";
}
/*
@@ -156,11 +157,11 @@ int DexlayoutDriver(int argc, char** argv) {
// Detect early problems.
if (optind == argc) {
- fprintf(stderr, "%s: no file specified\n", kProgramName);
+ LOG(ERROR) << "no file specified";
want_usage = true;
}
if (options.checksum_only_ && options.ignore_bad_checksum_) {
- fprintf(stderr, "Can't specify both -c and -i\n");
+ LOG(ERROR) << "Can't specify both -c and -i";
want_usage = true;
}
if (want_usage) {
@@ -173,7 +174,7 @@ int DexlayoutDriver(int argc, char** argv) {
if (options.output_file_name_) {
out_file = fopen(options.output_file_name_, "w");
if (!out_file) {
- fprintf(stderr, "Can't open %s\n", options.output_file_name_);
+ PLOG(ERROR) << "Can't open " << options.output_file_name_;
return 1;
}
}
@@ -183,12 +184,12 @@ int DexlayoutDriver(int argc, char** argv) {
if (options.profile_file_name_) {
int profile_fd = open(options.profile_file_name_, O_RDONLY);
if (profile_fd < 0) {
- fprintf(stderr, "Can't open %s\n", options.profile_file_name_);
+ PLOG(ERROR) << "Can't open " << options.profile_file_name_;
return 1;
}
profile_info.reset(new ProfileCompilationInfo());
if (!profile_info->Load(profile_fd)) {
- fprintf(stderr, "Can't read profile info from %s\n", options.profile_file_name_);
+ LOG(ERROR) << "Can't read profile info from " << options.profile_file_name_;
return 1;
}
}
@@ -213,5 +214,8 @@ int DexlayoutDriver(int argc, char** argv) {
} // namespace art
int main(int argc, char** argv) {
+ // Output all logging to stderr.
+ android::base::SetLogger(android::base::StderrLogger);
+
return art::DexlayoutDriver(argc, argv);
}
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index 3a7f9eeda6..be272fcf2c 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -27,6 +27,7 @@
#include "dex/code_item_accessors-inl.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_file_loader.h"
+#include "dexlayout.h"
#include "exec_utils.h"
#include "jit/profile_compilation_info.h"
#include "utils.h"
@@ -778,4 +779,58 @@ TEST_F(DexLayoutTest, LinkData) {
ASSERT_TRUE(::art::Exec(rm_exec_argv, &error_msg));
}
+TEST_F(DexLayoutTest, ClassFilter) {
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ std::string error_msg;
+ const ArtDexFileLoader dex_file_loader;
+ const std::string input_jar = GetTestDexFileName("ManyMethods");
+ CHECK(dex_file_loader.Open(input_jar.c_str(),
+ input_jar.c_str(),
+ /*verify*/ true,
+ /*verify_checksum*/ true,
+ &error_msg,
+ &dex_files)) << error_msg;
+ ASSERT_EQ(dex_files.size(), 1u);
+ for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
+ EXPECT_GT(dex_file->NumClassDefs(), 1u);
+ for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
+ LOG(INFO) << dex_file->GetClassDescriptor(class_def);
+ }
+ Options options;
+ // Filter out all the classes other than the one below based on class descriptor.
+ options.class_filter_.insert("LManyMethods$Strings;");
+ DexLayout dexlayout(options,
+ /*info*/ nullptr,
+ /*out_file*/ nullptr,
+ /*header*/ nullptr);
+ std::unique_ptr<DexContainer> out;
+ dexlayout.ProcessDexFile(dex_file->GetLocation().c_str(),
+ dex_file.get(),
+ /*dex_file_index*/ 0,
+ &out);
+ std::unique_ptr<const DexFile> output_dex_file(
+ dex_file_loader.OpenWithDataSection(
+ out->GetMainSection()->Begin(),
+ out->GetMainSection()->Size(),
+ out->GetDataSection()->Begin(),
+ out->GetDataSection()->Size(),
+ dex_file->GetLocation().c_str(),
+ /* checksum */ 0,
+ /*oat_dex_file*/ nullptr,
+ /* verify */ true,
+ /*verify_checksum*/ false,
+ &error_msg));
+ ASSERT_TRUE(output_dex_file != nullptr);
+
+ ASSERT_EQ(output_dex_file->NumClassDefs(), options.class_filter_.size());
+ for (uint32_t i = 0; i < output_dex_file->NumClassDefs(); ++i) {
+ // Check that every class in the output dex file is in the filter.
+ const DexFile::ClassDef& class_def = output_dex_file->GetClassDef(i);
+ ASSERT_TRUE(options.class_filter_.find(output_dex_file->GetClassDescriptor(class_def)) !=
+ options.class_filter_.end());
+ }
+ }
+}
+
} // namespace art
diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc
index 8daaef19dc..31a146d90e 100644
--- a/dexlist/dexlist.cc
+++ b/dexlist/dexlist.cc
@@ -32,7 +32,9 @@
#include <sys/stat.h>
#include <unistd.h>
-#include "dex/code_item_accessors-no_art-inl.h"
+#include <android-base/logging.h>
+
+#include "dex/code_item_accessors-inl.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_file_loader.h"
@@ -207,16 +209,14 @@ static int processFile(const char* fileName) {
size_t size = 0;
std::string error_msg;
if (!openAndMapFile(fileName, &base, &size, &error_msg)) {
- fputs(error_msg.c_str(), stderr);
- fputc('\n', stderr);
+ LOG(ERROR) << error_msg;
return -1;
}
std::vector<std::unique_ptr<const DexFile>> dex_files;
const DexFileLoader dex_file_loader;
if (!dex_file_loader.OpenAll(
base, size, fileName, /*verify*/ true, kVerifyChecksum, &error_msg, &dex_files)) {
- fputs(error_msg.c_str(), stderr);
- fputc('\n', stderr);
+ LOG(ERROR) << error_msg;
return -1;
}
@@ -237,9 +237,9 @@ static int processFile(const char* fileName) {
* Shows usage.
*/
static void usage(void) {
- fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
- fprintf(stderr, "%s: [-m p.c.m] [-o outfile] dexfile...\n", gProgName);
- fprintf(stderr, "\n");
+ LOG(ERROR) << "Copyright (C) 2007 The Android Open Source Project\n";
+ LOG(ERROR) << gProgName << ": [-m p.c.m] [-o outfile] dexfile...";
+ LOG(ERROR) << "";
}
/*
@@ -268,7 +268,7 @@ int dexlistDriver(int argc, char** argv) {
gOptions.argCopy = strdup(optarg);
char* meth = strrchr(gOptions.argCopy, '.');
if (meth == nullptr) {
- fprintf(stderr, "Expected: package.Class.method\n");
+ LOG(ERROR) << "Expected: package.Class.method";
wantUsage = true;
} else {
*meth = '\0';
@@ -285,7 +285,7 @@ int dexlistDriver(int argc, char** argv) {
// Detect early problems.
if (optind == argc) {
- fprintf(stderr, "%s: no file specified\n", gProgName);
+ LOG(ERROR) << "No file specified";
wantUsage = true;
}
if (wantUsage) {
@@ -298,7 +298,7 @@ int dexlistDriver(int argc, char** argv) {
if (gOptions.outputFileName) {
gOutFile = fopen(gOptions.outputFileName, "w");
if (!gOutFile) {
- fprintf(stderr, "Can't open %s\n", gOptions.outputFileName);
+ PLOG(ERROR) << "Can't open " << gOptions.outputFileName;
free(gOptions.argCopy);
return 1;
}
@@ -318,6 +318,9 @@ int dexlistDriver(int argc, char** argv) {
} // namespace art
int main(int argc, char** argv) {
+ // Output all logging to stderr.
+ android::base::SetLogger(android::base::StderrLogger);
+
return art::dexlistDriver(argc, argv);
}
diff --git a/dt_fd_forward/dt_fd_forward.cc b/dt_fd_forward/dt_fd_forward.cc
index 1cf31a75a5..116cdf84ed 100644
--- a/dt_fd_forward/dt_fd_forward.cc
+++ b/dt_fd_forward/dt_fd_forward.cc
@@ -276,17 +276,16 @@ static void SendAcceptMessage(int fd) {
TEMP_FAILURE_RETRY(send(fd, kAcceptMessage, sizeof(kAcceptMessage), MSG_EOR));
}
-IOResult FdForwardTransport::ReceiveFdsFromSocket() {
+IOResult FdForwardTransport::ReceiveFdsFromSocket(bool* do_handshake) {
union {
cmsghdr cm;
uint8_t buffer[CMSG_SPACE(sizeof(FdSet))];
} msg_union;
- // We don't actually care about the data. Only FDs. We need to have an iovec anyway to tell if we
- // got the values or not though.
- char dummy = '\0';
+ // This lets us know if we need to do a handshake or not.
+ char message[128];
iovec iov;
- iov.iov_base = &dummy;
- iov.iov_len = sizeof(dummy);
+ iov.iov_base = message;
+ iov.iov_len = sizeof(message);
msghdr msg;
memset(&msg, 0, sizeof(msg));
@@ -307,8 +306,22 @@ IOResult FdForwardTransport::ReceiveFdsFromSocket() {
return IOResult::kError;
}
FdSet out_fds = FdSet::ReadData(CMSG_DATA(cmsg));
- if (out_fds.read_fd_ < 0 || out_fds.write_fd_ < 0 || out_fds.write_lock_fd_ < 0) {
+ bool failed = false;
+ if (out_fds.read_fd_ < 0 ||
+ out_fds.write_fd_ < 0 ||
+ out_fds.write_lock_fd_ < 0) {
DT_IO_ERROR("Received fds were invalid!");
+ failed = true;
+ } else if (strcmp(kPerformHandshakeMessage, message) == 0) {
+ *do_handshake = true;
+ } else if (strcmp(kSkipHandshakeMessage, message) == 0) {
+ *do_handshake = false;
+ } else {
+ DT_IO_ERROR("Unknown message sent with fds.");
+ failed = true;
+ }
+
+ if (failed) {
if (out_fds.read_fd_ >= 0) {
close(out_fds.read_fd_);
}
@@ -346,8 +359,9 @@ jdwpTransportError FdForwardTransport::Accept() {
state_cv_.wait(lk);
}
+ bool do_handshake = false;
DCHECK_NE(listen_fd_.get(), -1);
- if (ReceiveFdsFromSocket() != IOResult::kOk) {
+ if (ReceiveFdsFromSocket(&do_handshake) != IOResult::kOk) {
CHECK(ChangeState(TransportState::kOpening, TransportState::kListening));
return ERR(IO_ERROR);
}
@@ -355,24 +369,27 @@ jdwpTransportError FdForwardTransport::Accept() {
current_seq_num_++;
// Moved to the opening state.
- char handshake_recv[sizeof(kJdwpHandshake)];
- memset(handshake_recv, 0, sizeof(handshake_recv));
- IOResult res = ReadFullyWithoutChecks(handshake_recv, sizeof(handshake_recv));
- if (res != IOResult::kOk ||
- strncmp(handshake_recv, kJdwpHandshake, sizeof(kJdwpHandshake)) != 0) {
- DT_IO_ERROR("Failed to read handshake");
- CHECK(ChangeState(TransportState::kOpening, TransportState::kListening));
- CloseFdsLocked();
- // Retry.
- continue;
- }
- res = WriteFullyWithoutChecks(kJdwpHandshake, sizeof(kJdwpHandshake));
- if (res != IOResult::kOk) {
- DT_IO_ERROR("Failed to write handshake");
- CHECK(ChangeState(TransportState::kOpening, TransportState::kListening));
- CloseFdsLocked();
- // Retry.
- continue;
+ if (do_handshake) {
+ // Perform the handshake
+ char handshake_recv[sizeof(kJdwpHandshake)];
+ memset(handshake_recv, 0, sizeof(handshake_recv));
+ IOResult res = ReadFullyWithoutChecks(handshake_recv, sizeof(handshake_recv));
+ if (res != IOResult::kOk ||
+ strncmp(handshake_recv, kJdwpHandshake, sizeof(kJdwpHandshake)) != 0) {
+ DT_IO_ERROR("Failed to read handshake");
+ CHECK(ChangeState(TransportState::kOpening, TransportState::kListening));
+ CloseFdsLocked();
+ // Retry.
+ continue;
+ }
+ res = WriteFullyWithoutChecks(kJdwpHandshake, sizeof(kJdwpHandshake));
+ if (res != IOResult::kOk) {
+ DT_IO_ERROR("Failed to write handshake");
+ CHECK(ChangeState(TransportState::kOpening, TransportState::kListening));
+ CloseFdsLocked();
+ // Retry.
+ continue;
+ }
}
break;
}
diff --git a/dt_fd_forward/dt_fd_forward.h b/dt_fd_forward/dt_fd_forward.h
index 9303c59acd..07a574bfa0 100644
--- a/dt_fd_forward/dt_fd_forward.h
+++ b/dt_fd_forward/dt_fd_forward.h
@@ -105,7 +105,9 @@ class FdForwardTransport : public jdwpTransportEnv {
bool ChangeState(TransportState old_state, TransportState new_state); // REQUIRES(state_mutex_);
- IOResult ReceiveFdsFromSocket();
+ // Gets the fds from the server side. do_handshake returns whether the transport can skip the
+ // jdwp handshake.
+ IOResult ReceiveFdsFromSocket(/*out*/bool* do_handshake);
IOResult WriteFully(const void* data, size_t ndata); // REQUIRES(!state_mutex_);
IOResult WriteFullyWithoutChecks(const void* data, size_t ndata); // REQUIRES(state_mutex_);
diff --git a/dt_fd_forward/export/fd_transport.h b/dt_fd_forward/export/fd_transport.h
index 245f0c2275..144ac5c6ec 100644
--- a/dt_fd_forward/export/fd_transport.h
+++ b/dt_fd_forward/export/fd_transport.h
@@ -47,6 +47,12 @@ struct FdSet {
}
};
+// Sent with the file descriptors if the transport should not skip waiting for the handshake.
+static constexpr char kPerformHandshakeMessage[] = "HANDSHAKE:REQD";
+
+// Sent with the file descriptors if the transport can skip waiting for the handshake.
+static constexpr char kSkipHandshakeMessage[] = "HANDSHAKE:SKIP";
+
// This message is sent over the fd associated with the transport when we are listening for fds.
static constexpr char kListenStartMessage[] = "dt_fd_forward:START-LISTEN";
diff --git a/oatdump/Android.bp b/oatdump/Android.bp
index 4851722734..c93c172eb4 100644
--- a/oatdump/Android.bp
+++ b/oatdump/Android.bp
@@ -36,6 +36,7 @@ art_cc_binary {
"libart",
"libart-compiler",
"libart-disassembler",
+ "libdexfile",
"libbase",
],
}
@@ -50,6 +51,7 @@ art_cc_binary {
"libartd",
"libartd-compiler",
"libartd-disassembler",
+ "libdexfile",
"libbase",
],
}
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index f7151b350d..6c9f569b19 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -26,6 +26,7 @@
#include <unordered_set>
#include <vector>
+#include "android-base/logging.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
@@ -2547,7 +2548,7 @@ class ImageDumper {
}
}
} else {
- CodeItemDataAccessor code_item_accessor(method);
+ CodeItemDataAccessor code_item_accessor(method->DexInstructionData());
size_t dex_instruction_bytes = code_item_accessor.InsnsSizeInCodeUnits() * 2;
stats_.dex_instruction_bytes += dex_instruction_bytes;
@@ -2917,7 +2918,7 @@ static int DumpImage(gc::space::ImageSpace* image_space,
std::ostream* os) REQUIRES_SHARED(Locks::mutator_lock_) {
const ImageHeader& image_header = image_space->GetImageHeader();
if (!image_header.IsValid()) {
- fprintf(stderr, "Invalid image header %s\n", image_space->GetImageLocation().c_str());
+ LOG(ERROR) << "Invalid image header " << image_space->GetImageLocation();
return EXIT_FAILURE;
}
ImageDumper image_dumper(os, *image_space, image_header, options);
@@ -3070,7 +3071,7 @@ static int DumpOat(Runtime* runtime,
dex_filename,
&error_msg));
if (oat_file == nullptr) {
- fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
+ LOG(ERROR) << "Failed to open oat file from '" << oat_filename << "': " << error_msg;
return EXIT_FAILURE;
}
@@ -3095,7 +3096,7 @@ static int SymbolizeOat(const char* oat_filename,
dex_filename,
&error_msg));
if (oat_file == nullptr) {
- fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
+ LOG(ERROR) << "Failed to open oat file from '" << oat_filename << "': " << error_msg;
return EXIT_FAILURE;
}
@@ -3110,7 +3111,7 @@ static int SymbolizeOat(const char* oat_filename,
result = oat_symbolizer.Symbolize();
}
if (!result) {
- fprintf(stderr, "Failed to symbolize\n");
+ LOG(ERROR) << "Failed to symbolize";
return EXIT_FAILURE;
}
@@ -3142,7 +3143,7 @@ class IMTDumper {
dex_filename,
&error_msg));
if (oat_file == nullptr) {
- fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
+ LOG(ERROR) << "Failed to open oat file from '" << oat_filename << "': " << error_msg;
return false;
}
@@ -3827,6 +3828,9 @@ struct OatdumpMain : public CmdlineMain<OatdumpArgs> {
} // namespace art
int main(int argc, char** argv) {
+ // Output all logging to stderr.
+ android::base::SetLogger(android::base::StderrLogger);
+
art::OatdumpMain main;
return main.Main(argc, argv);
}
diff --git a/openjdkjvmti/Android.bp b/openjdkjvmti/Android.bp
index 0283999d54..1500bcae24 100644
--- a/openjdkjvmti/Android.bp
+++ b/openjdkjvmti/Android.bp
@@ -58,6 +58,7 @@ cc_defaults {
"libopenjdkjvmti_headers",
],
shared_libs: [
+ "libdexfile",
"libbase",
],
}
diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc
index cc56a7b714..a8d2a37fa6 100644
--- a/openjdkjvmti/fixed_up_dex_file.cc
+++ b/openjdkjvmti/fixed_up_dex_file.cc
@@ -33,6 +33,7 @@
#include "dex/art_dex_file_loader.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_file_loader.h"
+#include "dex/dex_file_verifier.h"
// Runtime includes.
#include "dex_container.h"
@@ -67,73 +68,75 @@ static void DoDexUnquicken(const art::DexFile& new_dex_file, const art::DexFile&
vdex->UnquickenDexFile(new_dex_file, original_dex_file, /* decompile_return_instruction */true);
}
-std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(const art::DexFile& original) {
+static void DCheckVerifyDexFile(const art::DexFile& dex) {
+ if (art::kIsDebugBuild) {
+ std::string error;
+ if (!art::DexFileVerifier::Verify(&dex,
+ dex.Begin(),
+ dex.Size(),
+ "FixedUpDexFile_Verification.dex",
+ /*verify_checksum*/ true,
+ &error)) {
+ LOG(FATAL) << "Failed to verify de-quickened dex file: " << error;
+ }
+ }
+}
+
+std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(const art::DexFile& original,
+ const char* descriptor) {
// Copy the data into mutable memory.
std::vector<unsigned char> data;
- if (original.IsCompactDexFile()) {
- // Compact dex has a separate data section that is relative from the original dex.
- // We need to copy the shared data section so that dequickening doesn't change anything.
- data.resize(original.Size() + original.DataSize());
- memcpy(data.data(), original.Begin(), original.Size());
- memcpy(data.data() + original.Size(), original.DataBegin(), original.DataSize());
- // Go patch up the header to point to the copied data section.
- art::CompactDexFile::Header* const header =
- const_cast<art::CompactDexFile::Header*>(art::CompactDexFile::Header::At(data.data()));
- header->data_off_ = original.Size();
- header->data_size_ = original.DataSize();
- } else {
- data.resize(original.Size());
- memcpy(data.data(), original.Begin(), original.Size());
- }
+ std::unique_ptr<const art::DexFile> new_dex_file;
std::string error;
const art::ArtDexFileLoader dex_file_loader;
- std::unique_ptr<const art::DexFile> new_dex_file(dex_file_loader.Open(
- data.data(),
- data.size(),
- /*location*/"Unquickening_dexfile.dex",
- /*location_checksum*/0,
- /*oat_dex_file*/nullptr,
- /*verify*/false,
- /*verify_checksum*/false,
- &error));
- if (new_dex_file.get() == nullptr) {
- LOG(ERROR) << "Unable to open dex file from memory for unquickening! error: " << error;
- return nullptr;
- }
-
- DoDexUnquicken(*new_dex_file, original);
if (original.IsCompactDexFile()) {
- // Since we are supposed to return a standard dex, convert back using dexlayout.
+ // Since we are supposed to return a standard dex, convert back using dexlayout. It's OK to do
+ // this before unquickening.
art::Options options;
options.compact_dex_level_ = art::CompactDexLevel::kCompactDexLevelNone;
- options.update_checksum_ = true;
+ // Add a filter to only include the class that has the matching descriptor.
+ static constexpr bool kFilterByDescriptor = true;
+ if (kFilterByDescriptor) {
+ options.class_filter_.insert(descriptor);
+ }
art::DexLayout dex_layout(options,
/*info*/ nullptr,
/*out_file*/ nullptr,
/*header*/ nullptr);
std::unique_ptr<art::DexContainer> dex_container;
- dex_layout.ProcessDexFile(new_dex_file->GetLocation().c_str(),
- new_dex_file.get(),
+ dex_layout.ProcessDexFile(original.GetLocation().c_str(),
+ &original,
0,
&dex_container);
art::DexContainer::Section* main_section = dex_container->GetMainSection();
CHECK_EQ(dex_container->GetDataSection()->Size(), 0u);
- // Overwrite the dex file stored in data with the new result.
- data.clear();
data.insert(data.end(), main_section->Begin(), main_section->End());
- new_dex_file = dex_file_loader.Open(
- data.data(),
- data.size(),
- /*location*/"Unquickening_dexfile.dex",
- /*location_checksum*/0,
- /*oat_dex_file*/nullptr,
- /*verify*/false,
- /*verify_checksum*/false,
- &error);
+ } else {
+ data.resize(original.Size());
+ memcpy(data.data(), original.Begin(), original.Size());
}
+ // Open the dex file in the buffer.
+ new_dex_file = dex_file_loader.Open(
+ data.data(),
+ data.size(),
+ /*location*/"Unquickening_dexfile.dex",
+ /*location_checksum*/0,
+ /*oat_dex_file*/nullptr,
+ /*verify*/false,
+ /*verify_checksum*/false,
+ &error);
+
+ if (new_dex_file == nullptr) {
+ LOG(ERROR) << "Unable to open dex file from memory for unquickening! error: " << error;
+ return nullptr;
+ }
+
+ DoDexUnquicken(*new_dex_file, original);
+
RecomputeDexChecksum(const_cast<art::DexFile*>(new_dex_file.get()));
+ DCheckVerifyDexFile(*new_dex_file);
std::unique_ptr<FixedUpDexFile> ret(new FixedUpDexFile(std::move(new_dex_file), std::move(data)));
return ret;
}
diff --git a/openjdkjvmti/fixed_up_dex_file.h b/openjdkjvmti/fixed_up_dex_file.h
index b8f349cf8c..7f05a2930a 100644
--- a/openjdkjvmti/fixed_up_dex_file.h
+++ b/openjdkjvmti/fixed_up_dex_file.h
@@ -49,7 +49,8 @@ namespace openjdkjvmti {
// are running on.
class FixedUpDexFile {
public:
- static std::unique_ptr<FixedUpDexFile> Create(const art::DexFile& original)
+ static std::unique_ptr<FixedUpDexFile> Create(const art::DexFile& original,
+ const char* descriptor)
REQUIRES_SHARED(art::Locks::mutator_lock_);
const art::DexFile& GetDexFile() {
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc
index b3f5c1886e..4d54d756d5 100644
--- a/openjdkjvmti/ti_class.cc
+++ b/openjdkjvmti/ti_class.cc
@@ -184,73 +184,27 @@ struct ClassCallback : public art::ClassLoadCallback {
return;
}
- // Strip the 'L' and ';' from the descriptor
- std::string name(std::string(descriptor).substr(1, strlen(descriptor) - 2));
-
art::Thread* self = art::Thread::Current();
- art::JNIEnvExt* env = self->GetJniEnv();
- ScopedLocalRef<jobject> loader(
- env, class_loader.IsNull() ? nullptr : env->AddLocalReference<jobject>(class_loader.Get()));
- std::unique_ptr<FixedUpDexFile> dex_file_copy(FixedUpDexFile::Create(initial_dex_file));
-
- // Go back to native.
- art::ScopedThreadSuspension sts(self, art::ThreadState::kNative);
- // Call all Non-retransformable agents.
- jint post_no_redefine_len = 0;
- unsigned char* post_no_redefine_dex_data = nullptr;
- std::unique_ptr<const unsigned char, FakeJvmtiDeleter<const unsigned char>>
- post_no_redefine_unique_ptr(nullptr, FakeJvmtiDeleter<const unsigned char>());
- event_handler->DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
- self,
- static_cast<JNIEnv*>(env),
- static_cast<jclass>(nullptr), // The class doesn't really exist yet so send null.
- loader.get(),
- name.c_str(),
- static_cast<jobject>(nullptr), // Android doesn't seem to have protection domains
- static_cast<jint>(dex_file_copy->Size()),
- static_cast<const unsigned char*>(dex_file_copy->Begin()),
- static_cast<jint*>(&post_no_redefine_len),
- static_cast<unsigned char**>(&post_no_redefine_dex_data));
- if (post_no_redefine_dex_data == nullptr) {
- DCHECK_EQ(post_no_redefine_len, 0);
- post_no_redefine_dex_data = const_cast<unsigned char*>(dex_file_copy->Begin());
- post_no_redefine_len = dex_file_copy->Size();
- } else {
- post_no_redefine_unique_ptr =
- std::unique_ptr<const unsigned char, FakeJvmtiDeleter<const unsigned char>>(
- post_no_redefine_dex_data, FakeJvmtiDeleter<const unsigned char>());
- DCHECK_GT(post_no_redefine_len, 0);
+ ArtClassDefinition def;
+ def.InitFirstLoad(descriptor, class_loader, initial_dex_file);
+
+ // Call all non-retransformable agents.
+ Transformer::TransformSingleClassDirect<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
+ event_handler, self, &def);
+
+ std::vector<unsigned char> post_non_retransform;
+ if (def.IsModified()) {
+ // Copy the dex data after the non-retransformable events.
+ post_non_retransform.resize(def.GetDexData().size());
+ memcpy(post_non_retransform.data(), def.GetDexData().data(), post_non_retransform.size());
}
+
// Call all retransformable agents.
- jint final_len = 0;
- unsigned char* final_dex_data = nullptr;
- std::unique_ptr<const unsigned char, FakeJvmtiDeleter<const unsigned char>>
- final_dex_unique_ptr(nullptr, FakeJvmtiDeleter<const unsigned char>());
- event_handler->DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
- self,
- static_cast<JNIEnv*>(env),
- static_cast<jclass>(nullptr), // The class doesn't really exist yet so send null.
- loader.get(),
- name.c_str(),
- static_cast<jobject>(nullptr), // Android doesn't seem to have protection domains
- static_cast<jint>(post_no_redefine_len),
- static_cast<const unsigned char*>(post_no_redefine_dex_data),
- static_cast<jint*>(&final_len),
- static_cast<unsigned char**>(&final_dex_data));
- if (final_dex_data == nullptr) {
- DCHECK_EQ(final_len, 0);
- final_dex_data = post_no_redefine_dex_data;
- final_len = post_no_redefine_len;
- } else {
- final_dex_unique_ptr =
- std::unique_ptr<const unsigned char, FakeJvmtiDeleter<const unsigned char>>(
- final_dex_data, FakeJvmtiDeleter<const unsigned char>());
- DCHECK_GT(final_len, 0);
- }
+ Transformer::TransformSingleClassDirect<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
+ event_handler, self, &def);
- if (final_dex_data != dex_file_copy->Begin()) {
+ if (def.IsModified()) {
LOG(WARNING) << "Changing class " << descriptor;
- art::ScopedObjectAccess soa(self);
art::StackHandleScope<2> hs(self);
// Save the results of all the non-retransformable agents.
// First allocate the ClassExt
@@ -267,7 +221,7 @@ struct ClassCallback : public art::ClassLoadCallback {
// Allocate the byte array to store the dex file bytes in.
art::MutableHandle<art::mirror::Object> arr(hs.NewHandle<art::mirror::Object>(nullptr));
- if (post_no_redefine_dex_data == dex_file_copy->Begin() && name != "java/lang/Long") {
+ if (post_non_retransform.empty() && strcmp(descriptor, "Ljava/lang/Long;") != 0) {
// we didn't have any non-retransformable agents. We can just cache a pointer to the
// initial_dex_file. It will be kept live by the class_loader.
jlong dex_ptr = reinterpret_cast<uintptr_t>(&initial_dex_file);
@@ -277,8 +231,8 @@ struct ClassCallback : public art::ClassLoadCallback {
} else {
arr.Assign(art::mirror::ByteArray::AllocateAndFill(
self,
- reinterpret_cast<const signed char*>(post_no_redefine_dex_data),
- post_no_redefine_len));
+ reinterpret_cast<const signed char*>(post_non_retransform.data()),
+ post_non_retransform.size()));
}
if (arr.IsNull()) {
LOG(WARNING) << "Unable to allocate memory for initial dex-file. Aborting transformation";
@@ -289,8 +243,8 @@ struct ClassCallback : public art::ClassLoadCallback {
std::unique_ptr<const art::DexFile> dex_file(MakeSingleDexFile(self,
descriptor,
initial_dex_file.GetLocation(),
- final_len,
- final_dex_data));
+ def.GetDexData().size(),
+ def.GetDexData().data()));
if (dex_file.get() == nullptr) {
return;
}
diff --git a/openjdkjvmti/ti_class_definition.cc b/openjdkjvmti/ti_class_definition.cc
index 6560570136..7f2f80009a 100644
--- a/openjdkjvmti/ti_class_definition.cc
+++ b/openjdkjvmti/ti_class_definition.cc
@@ -46,29 +46,33 @@
namespace openjdkjvmti {
bool ArtClassDefinition::IsModified() const {
- // RedefineClasses calls always are 'modified' since they need to change the original_dex_file of
+ // RedefineClasses calls always are 'modified' since they need to change the current_dex_file of
// the class.
if (redefined_) {
return true;
}
+
+ // Check to see if any change has taken place.
+ if (current_dex_file_.data() == dex_data_.data()) {
+ // no change at all.
+ return false;
+ }
+
// Check if the dex file we want to set is the same as the current one.
// Unfortunately we need to do this check even if no modifications have been done since it could
// be that agents were removed in the mean-time so we still have a different dex file. The dex
// checksum means this is likely to be fairly fast.
- return static_cast<jint>(original_dex_file_.size()) != dex_len_ ||
- memcmp(original_dex_file_.data(), dex_data_.get(), dex_len_) != 0;
+ return current_dex_file_.size() != dex_data_.size() ||
+ memcmp(current_dex_file_.data(), dex_data_.data(), current_dex_file_.size()) != 0;
}
-jvmtiError ArtClassDefinition::InitCommon(ArtJvmTiEnv* env, jclass klass) {
- JNIEnv* jni_env = GetJniEnv(env);
- if (jni_env == nullptr) {
- return ERR(INTERNAL);
- }
- art::ScopedObjectAccess soa(jni_env);
+jvmtiError ArtClassDefinition::InitCommon(art::Thread* self, jclass klass) {
+ art::ScopedObjectAccess soa(self);
art::ObjPtr<art::mirror::Class> m_klass(soa.Decode<art::mirror::Class>(klass));
if (m_klass.IsNull()) {
return ERR(INVALID_CLASS);
}
+ initialized_ = true;
klass_ = klass;
loader_ = soa.AddLocalReference<jobject>(m_klass->GetClassLoader());
std::string descriptor_store;
@@ -79,11 +83,18 @@ jvmtiError ArtClassDefinition::InitCommon(ArtJvmTiEnv* env, jclass klass) {
return OK;
}
+static void DequickenDexFile(const art::DexFile* dex_file,
+ const char* descriptor,
+ /*out*/std::vector<unsigned char>* dex_data)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ std::unique_ptr<FixedUpDexFile> fixed_dex_file(FixedUpDexFile::Create(*dex_file, descriptor));
+ dex_data->resize(fixed_dex_file->Size());
+ memcpy(dex_data->data(), fixed_dex_file->Begin(), fixed_dex_file->Size());
+}
+
// Gets the data surrounding the given class.
-static jvmtiError GetDexDataForRetransformation(ArtJvmTiEnv* env,
- art::Handle<art::mirror::Class> klass,
- /*out*/jint* dex_data_len,
- /*out*/unsigned char** dex_data)
+static void GetDexDataForRetransformation(art::Handle<art::mirror::Class> klass,
+ /*out*/std::vector<unsigned char>* dex_data)
REQUIRES_SHARED(art::Locks::mutator_lock_) {
art::StackHandleScope<3> hs(art::Thread::Current());
art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->GetExtData()));
@@ -95,12 +106,9 @@ static jvmtiError GetDexDataForRetransformation(ArtJvmTiEnv* env,
DCHECK(orig_dex->GetClass()->GetComponentType()->IsPrimitiveByte());
art::Handle<art::mirror::ByteArray> orig_dex_bytes(
hs.NewHandle(art::down_cast<art::mirror::ByteArray*>(orig_dex->AsArray())));
- *dex_data_len = static_cast<jint>(orig_dex_bytes->GetLength());
- return CopyDataIntoJvmtiBuffer(
- env,
- reinterpret_cast<const unsigned char*>(orig_dex_bytes->GetData()),
- *dex_data_len,
- /*out*/dex_data);
+ dex_data->resize(orig_dex_bytes->GetLength());
+ memcpy(dex_data->data(), orig_dex_bytes->GetData(), dex_data->size());
+ return;
} else if (orig_dex->IsDexCache()) {
dex_file = orig_dex->AsDexCache()->GetDexFile();
} else {
@@ -113,7 +121,7 @@ static jvmtiError GetDexDataForRetransformation(ArtJvmTiEnv* env,
art::JValue val;
if (!art::UnboxPrimitiveForResult(orig_dex.Get(), prim_long_class, &val)) {
// This should never happen.
- return ERR(INTERNAL);
+ LOG(FATAL) << "Unable to unbox a primitive long value!";
}
dex_file = reinterpret_cast<const art::DexFile*>(static_cast<uintptr_t>(val.GetJ()));
}
@@ -122,58 +130,154 @@ static jvmtiError GetDexDataForRetransformation(ArtJvmTiEnv* env,
if (dex_file == nullptr) {
dex_file = &klass->GetDexFile();
}
- std::unique_ptr<FixedUpDexFile> fixed_dex_file(FixedUpDexFile::Create(*dex_file));
- *dex_data_len = static_cast<jint>(fixed_dex_file->Size());
- return CopyDataIntoJvmtiBuffer(env,
- fixed_dex_file->Begin(),
- fixed_dex_file->Size(),
- /*out*/dex_data);
+ std::string storage;
+ DequickenDexFile(dex_file, klass->GetDescriptor(&storage), dex_data);
}
-jvmtiError ArtClassDefinition::Init(ArtJvmTiEnv* env, jclass klass) {
- jvmtiError res = InitCommon(env, klass);
+static bool DexNeedsDequickening(art::Handle<art::mirror::Class> klass,
+ /*out*/ bool* from_class_ext)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::ObjPtr<art::mirror::ClassExt> ext(klass->GetExtData());
+ if (ext.IsNull()) {
+ // We don't seem to have ever been redefined so be conservative and say we need de-quickening.
+ *from_class_ext = false;
+ return true;
+ }
+ art::ObjPtr<art::mirror::Object> orig_dex(ext->GetOriginalDexFile());
+ if (orig_dex.IsNull()) {
+ // We don't seem to have ever been redefined so be conservative and say we need de-quickening.
+ *from_class_ext = false;
+ return true;
+ } else if (!orig_dex->IsArrayInstance()) {
+ // We were redefined but the original is held in a dex-cache or dex file. This means that the
+ // original dex file is the one from the disk, which might be quickened.
+ DCHECK(orig_dex->IsDexCache() || orig_dex->GetClass()->DescriptorEquals("Ljava/lang/Long;"));
+ *from_class_ext = true;
+ return true;
+ } else {
+ // An array instance means the original-dex-file is from a redefineClasses which cannot have any
+ // quickening, so it's fine to use directly.
+ DCHECK(orig_dex->GetClass()->GetComponentType()->IsPrimitiveByte());
+ *from_class_ext = true;
+ return false;
+ }
+}
+
+static const art::DexFile* GetQuickenedDexFile(art::Handle<art::mirror::Class> klass)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::ObjPtr<art::mirror::ClassExt> ext(klass->GetExtData());
+ if (ext.IsNull() || ext->GetOriginalDexFile() == nullptr) {
+ return &klass->GetDexFile();
+ }
+
+ art::ObjPtr<art::mirror::Object> orig_dex(ext->GetOriginalDexFile());
+ DCHECK(!orig_dex->IsArrayInstance());
+ if (orig_dex->IsDexCache()) {
+ return orig_dex->AsDexCache()->GetDexFile();
+ }
+
+ DCHECK(orig_dex->GetClass()->DescriptorEquals("Ljava/lang/Long;"))
+ << "Expected java/lang/Long but found object of type "
+ << orig_dex->GetClass()->PrettyClass();
+ art::ObjPtr<art::mirror::Class> prim_long_class(
+ art::Runtime::Current()->GetClassLinker()->GetClassRoot(
+ art::ClassLinker::kPrimitiveLong));
+ art::JValue val;
+ if (!art::UnboxPrimitiveForResult(orig_dex.Ptr(), prim_long_class, &val)) {
+ LOG(FATAL) << "Unable to unwrap a long value!";
+ }
+ return reinterpret_cast<const art::DexFile*>(static_cast<uintptr_t>(val.GetJ()));
+}
+
+template<typename GetOriginalDexFile>
+void ArtClassDefinition::InitWithDex(GetOriginalDexFile get_original,
+ const art::DexFile* quick_dex) {
+ art::Thread* self = art::Thread::Current();
+ DCHECK(quick_dex != nullptr);
+ get_original(/*out*/&dex_data_memory_);
+ dex_data_ = art::ArrayRef<const unsigned char>(dex_data_memory_);
+ if (from_class_ext_) {
+ // We got initial from class_ext so the current one must have undergone redefinition so no
+ // cdex or quickening stuff.
+ // We can only do this if it's not a first load.
+ DCHECK(klass_ != nullptr);
+ const art::DexFile& cur_dex = self->DecodeJObject(klass_)->AsClass()->GetDexFile();
+ current_dex_file_ = art::ArrayRef<const unsigned char>(cur_dex.Begin(), cur_dex.Size());
+ } else {
+ // No redefinition must have ever happened so the (dequickened) cur_dex is the same as the
+ // initial dex_data. We need to copy it into another buffer to keep it around if we have a
+ // real redefinition.
+ current_dex_memory_.resize(dex_data_.size());
+ memcpy(current_dex_memory_.data(), dex_data_.data(), current_dex_memory_.size());
+ current_dex_file_ = art::ArrayRef<const unsigned char>(current_dex_memory_);
+ }
+}
+
+jvmtiError ArtClassDefinition::Init(art::Thread* self, jclass klass) {
+ jvmtiError res = InitCommon(self, klass);
if (res != OK) {
return res;
}
- unsigned char* new_data = nullptr;
- art::Thread* self = art::Thread::Current();
art::ScopedObjectAccess soa(self);
art::StackHandleScope<1> hs(self);
art::Handle<art::mirror::Class> m_klass(hs.NewHandle(self->DecodeJObject(klass)->AsClass()));
- res = GetDexDataForRetransformation(env, m_klass, &dex_len_, &new_data);
- if (res != OK) {
- return res;
- }
- dex_data_ = MakeJvmtiUniquePtr(env, new_data);
- if (m_klass->GetExtData() == nullptr || m_klass->GetExtData()->GetOriginalDexFile() == nullptr) {
- // We have never redefined class this yet. Keep track of what the (de-quickened) dex file looks
- // like so we can tell if anything has changed. Really we would like to just always do the
- // 'else' block but the fact that we de-quickened stuff screws us over.
- unsigned char* original_data_memory = nullptr;
- res = CopyDataIntoJvmtiBuffer(env, dex_data_.get(), dex_len_, &original_data_memory);
- original_dex_file_memory_ = MakeJvmtiUniquePtr(env, original_data_memory);
- original_dex_file_ = art::ArrayRef<const unsigned char>(original_data_memory, dex_len_);
- } else {
- // We know that we have been redefined at least once (there is an original_dex_file set in
- // the class) so we can just use the current dex file directly.
- const art::DexFile& dex_file = m_klass->GetDexFile();
- original_dex_file_ = art::ArrayRef<const unsigned char>(dex_file.Begin(), dex_file.Size());
+ if (!DexNeedsDequickening(m_klass, &from_class_ext_)) {
+ // We don't need to do any dequickening. We want to copy the data just so we don't need to deal
+ // with the GC moving it around.
+ art::ObjPtr<art::mirror::ByteArray> orig_dex(
+ m_klass->GetExtData()->GetOriginalDexFile()->AsByteArray());
+ dex_data_memory_.resize(orig_dex->GetLength());
+ memcpy(dex_data_memory_.data(), orig_dex->GetData(), dex_data_memory_.size());
+ dex_data_ = art::ArrayRef<const unsigned char>(dex_data_memory_);
+
+ // Since we are here we must not have any quickened instructions since we were redefined.
+ const art::DexFile& cur_dex = m_klass->GetDexFile();
+ DCHECK(from_class_ext_);
+ current_dex_file_ = art::ArrayRef<const unsigned char>(cur_dex.Begin(), cur_dex.Size());
+ return OK;
}
- return res;
+
+ // We need to dequicken stuff. This is often super slow (10's of ms). Instead we will do it
+ // dynamically.
+ const art::DexFile* quick_dex = GetQuickenedDexFile(m_klass);
+ auto get_original = [&](/*out*/std::vector<unsigned char>* dex_data)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ GetDexDataForRetransformation(m_klass, dex_data);
+ };
+ InitWithDex(get_original, quick_dex);
+ return OK;
}
-jvmtiError ArtClassDefinition::Init(ArtJvmTiEnv* env, const jvmtiClassDefinition& def) {
- jvmtiError res = InitCommon(env, def.klass);
+jvmtiError ArtClassDefinition::Init(art::Thread* self, const jvmtiClassDefinition& def) {
+ jvmtiError res = InitCommon(self, def.klass);
if (res != OK) {
return res;
}
- unsigned char* new_data = nullptr;
- original_dex_file_ = art::ArrayRef<const unsigned char>(def.class_bytes, def.class_byte_count);
+ // We are being directly redefined.
redefined_ = true;
- dex_len_ = def.class_byte_count;
- res = CopyDataIntoJvmtiBuffer(env, def.class_bytes, def.class_byte_count, /*out*/ &new_data);
- dex_data_ = MakeJvmtiUniquePtr(env, new_data);
- return res;
+ current_dex_file_ = art::ArrayRef<const unsigned char>(def.class_bytes, def.class_byte_count);
+ dex_data_ = art::ArrayRef<const unsigned char>(def.class_bytes, def.class_byte_count);
+ return OK;
+}
+
+void ArtClassDefinition::InitFirstLoad(const char* descriptor,
+ art::Handle<art::mirror::ClassLoader> klass_loader,
+ const art::DexFile& dex_file) {
+ art::Thread* self = art::Thread::Current();
+ art::ScopedObjectAccess soa(self);
+ initialized_ = true;
+ // No Class
+ klass_ = nullptr;
+ loader_ = soa.AddLocalReference<jobject>(klass_loader.Get());
+ std::string descriptor_str(descriptor);
+ name_ = descriptor_str.substr(1, descriptor_str.size() - 2);
+ // Android doesn't really have protection domains.
+ protection_domain_ = nullptr;
+ auto get_original = [&](/*out*/std::vector<unsigned char>* dex_data)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ DequickenDexFile(&dex_file, descriptor, dex_data);
+ };
+ InitWithDex(get_original, &dex_file);
}
} // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_class_definition.h b/openjdkjvmti/ti_class_definition.h
index accc456957..00847394e7 100644
--- a/openjdkjvmti/ti_class_definition.h
+++ b/openjdkjvmti/ti_class_definition.h
@@ -48,41 +48,49 @@ class ArtClassDefinition {
loader_(nullptr),
name_(),
protection_domain_(nullptr),
- dex_len_(0),
- dex_data_(nullptr),
- original_dex_file_memory_(nullptr),
- original_dex_file_(),
- redefined_(false) {}
-
- jvmtiError Init(ArtJvmTiEnv* env, jclass klass);
- jvmtiError Init(ArtJvmTiEnv* env, const jvmtiClassDefinition& def);
+ dex_data_memory_(),
+ dex_data_(),
+ current_dex_file_(),
+ redefined_(false),
+ from_class_ext_(false),
+ initialized_(false) {}
+
+ void InitFirstLoad(const char* descriptor,
+ art::Handle<art::mirror::ClassLoader> klass_loader,
+ const art::DexFile& dex_file);
+ jvmtiError Init(art::Thread* self, jclass klass);
+ jvmtiError Init(art::Thread* self, const jvmtiClassDefinition& def);
ArtClassDefinition(ArtClassDefinition&& o) = default;
ArtClassDefinition& operator=(ArtClassDefinition&& o) = default;
- void SetNewDexData(ArtJvmTiEnv* env, jint new_dex_len, unsigned char* new_dex_data) {
+ void SetNewDexData(jint new_dex_len, unsigned char* new_dex_data) {
DCHECK(IsInitialized());
if (new_dex_data == nullptr) {
return;
- } else if (new_dex_data != dex_data_.get() || new_dex_len != dex_len_) {
- dex_len_ = new_dex_len;
- dex_data_ = MakeJvmtiUniquePtr(env, new_dex_data);
+ } else {
+ art::ArrayRef<const unsigned char> new_data(new_dex_data, new_dex_len);
+ if (new_data != dex_data_) {
+ dex_data_memory_.resize(new_dex_len);
+ memcpy(dex_data_memory_.data(), new_dex_data, new_dex_len);
+ dex_data_ = art::ArrayRef<const unsigned char>(dex_data_memory_);
+ }
}
}
art::ArrayRef<const unsigned char> GetNewOriginalDexFile() const {
DCHECK(IsInitialized());
if (redefined_) {
- return original_dex_file_;
+ return current_dex_file_;
} else {
return art::ArrayRef<const unsigned char>();
}
}
- bool IsModified() const;
+ bool IsModified() const REQUIRES_SHARED(art::Locks::mutator_lock_);
bool IsInitialized() const {
- return klass_ != nullptr;
+ return initialized_;
}
jclass GetClass() const {
@@ -107,22 +115,43 @@ class ArtClassDefinition {
art::ArrayRef<const unsigned char> GetDexData() const {
DCHECK(IsInitialized());
- return art::ArrayRef<const unsigned char>(dex_data_.get(), dex_len_);
+ return dex_data_;
}
private:
- jvmtiError InitCommon(ArtJvmTiEnv* env, jclass klass);
+ jvmtiError InitCommon(art::Thread* self, jclass klass);
+
+ template<typename GetOriginalDexFile>
+ void InitWithDex(GetOriginalDexFile get_original, const art::DexFile* quick_dex)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
jclass klass_;
jobject loader_;
std::string name_;
jobject protection_domain_;
- jint dex_len_;
- JvmtiUniquePtr<unsigned char> dex_data_;
- JvmtiUniquePtr<unsigned char> original_dex_file_memory_;
- art::ArrayRef<const unsigned char> original_dex_file_;
+
+ // A unique_ptr to the current dex_data if it needs to be cleaned up.
+ std::vector<unsigned char> dex_data_memory_;
+
+ // A ref to the current dex data. This is either dex_data_memory_, or current_dex_file_. This is
+ // what the dex file will be turned into.
+ art::ArrayRef<const unsigned char> dex_data_;
+
+ // This is only used if we failed to create a mmap to store the dequickened data
+ std::vector<unsigned char> current_dex_memory_;
+
+ // This is a dequickened version of what is loaded right now. It is either current_dex_memory_ (if
+ // no other redefinition has ever happened to this) or the current dex_file_ directly (if this
+ // class has been redefined, thus it cannot have any quickened stuff).
+ art::ArrayRef<const unsigned char> current_dex_file_;
+
bool redefined_;
+ // If we got the initial dex_data_ from a class_ext
+ bool from_class_ext_;
+
+ bool initialized_;
+
DISALLOW_COPY_AND_ASSIGN(ArtClassDefinition);
};
diff --git a/openjdkjvmti/ti_class_loader.h b/openjdkjvmti/ti_class_loader.h
index 27ea3f5191..ceb7b331de 100644
--- a/openjdkjvmti/ti_class_loader.h
+++ b/openjdkjvmti/ti_class_loader.h
@@ -41,6 +41,7 @@
#include "base/array_slice.h"
#include "class_linker.h"
#include "dex/dex_file.h"
+#include "dex/utf.h"
#include "gc_root-inl.h"
#include "globals.h"
#include "jni_env_ext-inl.h"
@@ -60,7 +61,6 @@
#include "thread_list.h"
#include "ti_class_definition.h"
#include "transform.h"
-#include "utf.h"
#include "utils/dex_cache_arrays_layout-inl.h"
namespace openjdkjvmti {
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index 3f144c8f0f..83d64ef1d8 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -123,7 +123,7 @@ jvmtiError MethodUtil::GetBytecodes(jvmtiEnv* env,
}
art::ScopedObjectAccess soa(art::Thread::Current());
- art::CodeItemInstructionAccessor accessor(art_method);
+ art::CodeItemInstructionAccessor accessor(art_method->DexInstructions());
if (!accessor.HasCodeItem()) {
*size_ptr = 0;
*bytecode_ptr = nullptr;
@@ -168,7 +168,7 @@ jvmtiError MethodUtil::GetArgumentsSize(jvmtiEnv* env ATTRIBUTE_UNUSED,
}
DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
- *size_ptr = art::CodeItemDataAccessor(art_method).InsSize();
+ *size_ptr = art_method->DexInstructionData().InsSize();
return ERR(NONE);
}
@@ -200,7 +200,7 @@ jvmtiError MethodUtil::GetLocalVariableTable(jvmtiEnv* env,
// TODO HasCodeItem == false means that the method is abstract (or native, but we check that
// earlier). We should check what is returned by the RI in this situation since it's not clear
// what the appropriate return value is from the spec.
- art::CodeItemDebugInfoAccessor accessor(art_method);
+ art::CodeItemDebugInfoAccessor accessor(art_method->DexInstructionDebugInfo());
if (!accessor.HasCodeItem()) {
return ERR(ABSENT_INFORMATION);
}
@@ -301,7 +301,7 @@ jvmtiError MethodUtil::GetMaxLocals(jvmtiEnv* env ATTRIBUTE_UNUSED,
}
DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
- *max_ptr = art::CodeItemDataAccessor(art_method).RegistersSize();
+ *max_ptr = art_method->DexInstructionData().RegistersSize();
return ERR(NONE);
}
@@ -480,7 +480,7 @@ jvmtiError MethodUtil::GetLineNumberTable(jvmtiEnv* env,
return ERR(NULL_POINTER);
}
- accessor = art::CodeItemDebugInfoAccessor(art_method);
+ accessor = art::CodeItemDebugInfoAccessor(art_method->DexInstructionDebugInfo());
dex_file = art_method->GetDexFile();
DCHECK(accessor.HasCodeItem()) << art_method->PrettyMethod() << " " << dex_file->GetLocation();
}
@@ -567,7 +567,7 @@ class CommonLocalVariableClosure : public art::Closure {
// TODO It might be useful to fake up support for get at least on proxy frames.
result_ = ERR(OPAQUE_FRAME);
return;
- } else if (art::CodeItemDataAccessor(method).RegistersSize() <= slot_) {
+ } else if (method->DexInstructionData().RegistersSize() <= slot_) {
result_ = ERR(INVALID_SLOT);
return;
}
@@ -618,7 +618,7 @@ class CommonLocalVariableClosure : public art::Closure {
if (dex_file == nullptr) {
return ERR(OPAQUE_FRAME);
}
- art::CodeItemDebugInfoAccessor accessor(method);
+ art::CodeItemDebugInfoAccessor accessor(method->DexInstructionDebugInfo());
if (!accessor.HasCodeItem()) {
return ERR(OPAQUE_FRAME);
}
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index 717b2ba669..c3fb946b9a 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -351,15 +351,14 @@ jvmtiError Redefiner::RedefineClasses(ArtJvmTiEnv* env,
memcpy(class_bytes_copy, definitions[i].class_bytes, definitions[i].class_byte_count);
ArtClassDefinition def;
- res = def.Init(env, definitions[i]);
+ res = def.Init(self, definitions[i]);
if (res != OK) {
return res;
}
def_vector.push_back(std::move(def));
}
// Call all the transformation events.
- jvmtiError res = Transformer::RetransformClassesDirect(env,
- event_handler,
+ jvmtiError res = Transformer::RetransformClassesDirect(event_handler,
self,
&def_vector);
if (res != OK) {
diff --git a/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h
index b537e1b01c..c920707afd 100644
--- a/openjdkjvmti/ti_redefine.h
+++ b/openjdkjvmti/ti_redefine.h
@@ -41,6 +41,7 @@
#include "base/array_ref.h"
#include "class_linker.h"
#include "dex/dex_file.h"
+#include "dex/utf.h"
#include "gc_root-inl.h"
#include "globals.h"
#include "jni_env_ext-inl.h"
@@ -60,7 +61,6 @@
#include "thread_list.h"
#include "ti_class_definition.h"
#include "transform.h"
-#include "utf.h"
#include "utils/dex_cache_arrays_layout-inl.h"
namespace openjdkjvmti {
diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc
index bc77753f8f..373944f179 100644
--- a/openjdkjvmti/ti_stack.cc
+++ b/openjdkjvmti/ti_stack.cc
@@ -1045,7 +1045,7 @@ jvmtiError StackUtil::NotifyFramePop(jvmtiEnv* env, jthread thread, jint depth)
if (shadow_frame == nullptr) {
needs_instrument = true;
const size_t frame_id = visitor.GetFrameId();
- const uint16_t num_regs = art::CodeItemDataAccessor(method).RegistersSize();
+ const uint16_t num_regs = method->DexInstructionData().RegistersSize();
shadow_frame = target->FindOrCreateDebuggerShadowFrame(frame_id,
num_regs,
method,
diff --git a/openjdkjvmti/transform.cc b/openjdkjvmti/transform.cc
index 3a5fcccf35..af838d62b9 100644
--- a/openjdkjvmti/transform.cc
+++ b/openjdkjvmti/transform.cc
@@ -39,6 +39,7 @@
#include "class_linker.h"
#include "dex/dex_file.h"
#include "dex/dex_file_types.h"
+#include "dex/utf.h"
#include "events-inl.h"
#include "gc_root-inl.h"
#include "globals.h"
@@ -58,32 +59,50 @@
#include "thread_list.h"
#include "ti_redefine.h"
#include "transform.h"
-#include "utf.h"
#include "utils/dex_cache_arrays_layout-inl.h"
namespace openjdkjvmti {
+// Initialize templates.
+template
+void Transformer::TransformSingleClassDirect<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
+ EventHandler* event_handler, art::Thread* self, /*in-out*/ArtClassDefinition* def);
+template
+void Transformer::TransformSingleClassDirect<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
+ EventHandler* event_handler, art::Thread* self, /*in-out*/ArtClassDefinition* def);
+
+template<ArtJvmtiEvent kEvent>
+void Transformer::TransformSingleClassDirect(EventHandler* event_handler,
+ art::Thread* self,
+ /*in-out*/ArtClassDefinition* def) {
+ static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable ||
+ kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable,
+ "bad event type");
+ jint new_len = -1;
+ unsigned char* new_data = nullptr;
+ art::ArrayRef<const unsigned char> dex_data = def->GetDexData();
+ event_handler->DispatchEvent<kEvent>(
+ self,
+ static_cast<JNIEnv*>(self->GetJniEnv()),
+ def->GetClass(),
+ def->GetLoader(),
+ def->GetName().c_str(),
+ def->GetProtectionDomain(),
+ static_cast<jint>(dex_data.size()),
+ dex_data.data(),
+ /*out*/&new_len,
+ /*out*/&new_data);
+ def->SetNewDexData(new_len, new_data);
+}
+
jvmtiError Transformer::RetransformClassesDirect(
- ArtJvmTiEnv* env,
EventHandler* event_handler,
art::Thread* self,
/*in-out*/std::vector<ArtClassDefinition>* definitions) {
for (ArtClassDefinition& def : *definitions) {
- jint new_len = -1;
- unsigned char* new_data = nullptr;
- art::ArrayRef<const unsigned char> dex_data = def.GetDexData();
- event_handler->DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
- self,
- GetJniEnv(env),
- def.GetClass(),
- def.GetLoader(),
- def.GetName().c_str(),
- def.GetProtectionDomain(),
- static_cast<jint>(dex_data.size()),
- dex_data.data(),
- /*out*/&new_len,
- /*out*/&new_data);
- def.SetNewDexData(env, new_len, new_data);
+ TransformSingleClassDirect<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(event_handler,
+ self,
+ &def);
}
return OK;
}
@@ -120,13 +139,13 @@ jvmtiError Transformer::RetransformClasses(ArtJvmTiEnv* env,
return ERR(UNMODIFIABLE_CLASS);
}
ArtClassDefinition def;
- res = def.Init(env, classes[i]);
+ res = def.Init(self, classes[i]);
if (res != OK) {
return res;
}
definitions.push_back(std::move(def));
}
- res = RetransformClassesDirect(env, event_handler, self, &definitions);
+ res = RetransformClassesDirect(event_handler, self, &definitions);
if (res != OK) {
return res;
}
diff --git a/openjdkjvmti/transform.h b/openjdkjvmti/transform.h
index 6bbe60a91f..f43af174f0 100644
--- a/openjdkjvmti/transform.h
+++ b/openjdkjvmti/transform.h
@@ -48,8 +48,13 @@ jvmtiError GetClassLocation(ArtJvmTiEnv* env, jclass klass, /*out*/std::string*
class Transformer {
public:
+ template<ArtJvmtiEvent kEvent>
+ static void TransformSingleClassDirect(
+ EventHandler* event_handler,
+ art::Thread* self,
+ /*in-out*/ArtClassDefinition* def);
+
static jvmtiError RetransformClassesDirect(
- ArtJvmTiEnv* env,
EventHandler* event_handler,
art::Thread* self,
/*in-out*/std::vector<ArtClassDefinition>* definitions);
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 6c9cf864b3..d9cefee611 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -25,12 +25,14 @@
#include <string>
#include <vector>
+#include "android-base/file.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/dumpable.h"
+#include "base/file_utils.h"
#include "base/logging.h" // For InitLogging.
#include "base/memory_tool.h"
#include "base/scoped_flock.h"
@@ -235,6 +237,126 @@ static bool WriteRelFile(
return true;
}
+static bool CheckImageIdenticalToOriginalExceptForRelocation(
+ const std::string& relocated_filename,
+ const std::string& original_filename,
+ std::string* error_msg) {
+ *error_msg = "";
+ std::string rel_filename = original_filename + ".rel";
+ std::unique_ptr<File> rel_file(OS::OpenFileForReading(rel_filename.c_str()));
+ if (rel_file.get() == nullptr) {
+ *error_msg = StringPrintf("Failed to open image relocation file %s", rel_filename.c_str());
+ return false;
+ }
+ int64_t rel_size = rel_file->GetLength();
+ if (rel_size < 0) {
+ *error_msg = StringPrintf("Error while getting size of image relocation file %s",
+ rel_filename.c_str());
+ return false;
+ }
+ std::unique_ptr<uint8_t[]> rel(new uint8_t[rel_size]);
+ if (!rel_file->ReadFully(rel.get(), rel_size)) {
+ *error_msg = StringPrintf("Failed to read image relocation file %s", rel_filename.c_str());
+ return false;
+ }
+
+ std::unique_ptr<File> image_file(OS::OpenFileForReading(relocated_filename.c_str()));
+ if (image_file.get() == nullptr) {
+ *error_msg = StringPrintf("Unable to open relocated image file %s",
+ relocated_filename.c_str());
+ return false;
+ }
+
+ int64_t image_size = image_file->GetLength();
+ if (image_size < 0) {
+ *error_msg = StringPrintf("Error while getting size of relocated image file %s",
+ relocated_filename.c_str());
+ return false;
+ }
+ if ((image_size % 4) != 0) {
+ *error_msg =
+ StringPrintf(
+ "Relocated image file %s size not multiple of 4: %" PRId64,
+ relocated_filename.c_str(), image_size);
+ return false;
+ }
+ if (image_size > std::numeric_limits<uint32_t>::max()) {
+ *error_msg =
+ StringPrintf(
+ "Relocated image file %s too large: %" PRId64, relocated_filename.c_str(), image_size);
+ return false;
+ }
+
+ std::unique_ptr<uint8_t[]> image(new uint8_t[image_size]);
+ if (!image_file->ReadFully(image.get(), image_size)) {
+ *error_msg = StringPrintf("Failed to read relocated image file %s", relocated_filename.c_str());
+ return false;
+ }
+
+ const uint8_t* original_image_digest = rel.get();
+ if (rel_size < SHA256_DIGEST_LENGTH) {
+ *error_msg = StringPrintf("Malformed image relocation file %s: too short",
+ rel_filename.c_str());
+ return false;
+ }
+
+ const ImageHeader& image_header = *reinterpret_cast<const ImageHeader*>(image.get());
+ off_t expected_diff = image_header.GetPatchDelta();
+
+ if (expected_diff == 0) {
+ *error_msg = StringPrintf("Unsuported patch delta of zero in %s",
+ relocated_filename.c_str());
+ return false;
+ }
+
+ // Relocated image is expected to differ from the original due to relocation.
+ // Unrelocate the image in memory to compensate.
+ uint8_t* image_start = image.get();
+ const uint8_t* rel_end = &rel[rel_size];
+ const uint8_t* rel_ptr = &rel[SHA256_DIGEST_LENGTH];
+ // The remaining .rel file consists of offsets at which relocation should've occurred.
+ // For each offset, we "unrelocate" the image by subtracting the expected relocation
+ // diff value (as specified in the image header).
+ //
+ // Each offset is encoded as a delta/diff relative to the previous offset. With the
+ // very first offset being encoded relative to offset 0.
+ // Deltas are encoded using little-endian 7 bits per byte encoding, with all bytes except
+ // the last one having the highest bit set.
+ uint32_t offset = 0;
+ while (rel_ptr != rel_end) {
+ uint32_t offset_delta = 0;
+ if (DecodeUnsignedLeb128Checked(&rel_ptr, rel_end, &offset_delta)) {
+ offset += offset_delta;
+ uint32_t *image_value = reinterpret_cast<uint32_t*>(image_start + offset);
+ *image_value -= expected_diff;
+ } else {
+ *error_msg =
+ StringPrintf(
+ "Malformed image relocation file %s: "
+ "last byte has it's most significant bit set",
+ rel_filename.c_str());
+ return false;
+ }
+ }
+
+ // Image in memory is now supposed to be identical to the original. We
+ // confirm this by comparing the digest of the in-memory image to the expected
+ // digest from relocation file.
+ uint8_t image_digest[SHA256_DIGEST_LENGTH];
+ SHA256(image.get(), image_size, image_digest);
+ if (memcmp(image_digest, original_image_digest, SHA256_DIGEST_LENGTH) != 0) {
+ *error_msg =
+ StringPrintf(
+ "Relocated image %s does not match the original %s after unrelocation",
+ relocated_filename.c_str(),
+ original_filename.c_str());
+ return false;
+ }
+
+ // Relocated image is identical to the original, once relocations are taken into account
+ return true;
+}
+
bool PatchOat::Patch(const std::string& image_location,
off_t delta,
const std::string& output_image_directory,
@@ -475,6 +597,86 @@ bool PatchOat::Patch(const std::string& image_location,
return true;
}
+bool PatchOat::Verify(const std::string& image_location,
+ const std::string& output_image_directory,
+ InstructionSet isa,
+ TimingLogger* timings) {
+ if (image_location.empty()) {
+ LOG(ERROR) << "Original image file not provided";
+ return false;
+ }
+ if (output_image_directory.empty()) {
+ LOG(ERROR) << "Relocated image directory not provided";
+ return false;
+ }
+
+ TimingLogger::ScopedTiming t("Runtime Setup", timings);
+
+ CHECK_NE(isa, InstructionSet::kNone);
+ const char* isa_name = GetInstructionSetString(isa);
+
+ // Set up the runtime
+ RuntimeOptions options;
+ NoopCompilerCallbacks callbacks;
+ options.push_back(std::make_pair("compilercallbacks", &callbacks));
+ std::string img = "-Ximage:" + image_location;
+ options.push_back(std::make_pair(img.c_str(), nullptr));
+ options.push_back(std::make_pair("imageinstructionset", reinterpret_cast<const void*>(isa_name)));
+ options.push_back(std::make_pair("-Xno-sig-chain", nullptr));
+ if (!Runtime::Create(options, false)) {
+ LOG(ERROR) << "Unable to initialize runtime";
+ return false;
+ }
+ std::unique_ptr<Runtime> runtime(Runtime::Current());
+
+ // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
+ // give it away now and then switch to a more manageable ScopedObjectAccess.
+ Thread::Current()->TransitionFromRunnableToSuspended(kNative);
+ ScopedObjectAccess soa(Thread::Current());
+
+ t.NewTiming("Image Verification setup");
+ std::vector<gc::space::ImageSpace*> spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces();
+
+ // TODO: Check that no other .rel files exist in the original dir
+
+ bool success = true;
+ std::string image_location_dir = android::base::Dirname(image_location);
+ for (size_t i = 0; i < spaces.size(); ++i) {
+ gc::space::ImageSpace* space = spaces[i];
+ std::string image_filename = space->GetImageLocation();
+
+ std::string relocated_image_filename;
+ std::string error_msg;
+ if (!GetDalvikCacheFilename(image_filename.c_str(),
+ output_image_directory.c_str(), &relocated_image_filename, &error_msg)) {
+ LOG(ERROR) << "Failed to find relocated image file name: " << error_msg;
+ success = false;
+ break;
+ }
+ // location: /system/framework/boot.art
+ // isa: arm64
+ // basename: boot.art
+ // original: /system/framework/arm64/boot.art
+ // relocation: /system/framework/arm64/boot.art.rel
+ std::string original_image_filename = GetSystemImageFilename(image_filename.c_str(), isa);
+
+ if (!CheckImageIdenticalToOriginalExceptForRelocation(
+ relocated_image_filename, original_image_filename, &error_msg)) {
+ LOG(ERROR) << error_msg;
+ success = false;
+ break;
+ }
+ }
+
+ if (!kIsDebugBuild && !(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) {
+ // We want to just exit on non-debug builds, not bringing the runtime down
+ // in an orderly fashion. So release the following fields.
+ runtime.release();
+ }
+
+ return success;
+}
+
bool PatchOat::WriteImage(File* out) {
TimingLogger::ScopedTiming t("Writing image File", timings_);
std::string error_msg;
@@ -919,6 +1121,8 @@ NO_RETURN static void Usage(const char *fmt, ...) {
UsageError(" --base-offset-delta=<delta>: Specify the amount to change the old base-offset by.");
UsageError(" This value may be negative.");
UsageError("");
+ UsageError(" --verify: Verify an existing patched file instead of creating one.");
+ UsageError("");
UsageError(" --dump-timings: dump out patch timing information");
UsageError("");
UsageError(" --no-dump-timings: do not dump out patch timing information");
@@ -927,16 +1131,16 @@ NO_RETURN static void Usage(const char *fmt, ...) {
exit(EXIT_FAILURE);
}
-static int patchoat_image(TimingLogger& timings,
- InstructionSet isa,
- const std::string& input_image_location,
- const std::string& output_image_filename,
- const std::string& output_image_relocation_filename,
- off_t base_delta,
- bool base_delta_set,
- bool debug) {
+static int patchoat_patch_image(TimingLogger& timings,
+ InstructionSet isa,
+ const std::string& input_image_location,
+ const std::string& output_image_directory,
+ const std::string& output_image_relocation_filename,
+ off_t base_delta,
+ bool base_delta_set,
+ bool debug) {
CHECK(!input_image_location.empty());
- if ((output_image_filename.empty()) && (output_image_relocation_filename.empty())) {
+ if ((output_image_directory.empty()) && (output_image_relocation_filename.empty())) {
Usage("Image patching requires --output-image-file or --output-image-relocation-file");
}
@@ -956,8 +1160,6 @@ static int patchoat_image(TimingLogger& timings,
TimingLogger::ScopedTiming pt("patch image and oat", &timings);
- std::string output_image_directory =
- output_image_filename.substr(0, output_image_filename.find_last_of('/'));
std::string output_image_relocation_directory =
output_image_relocation_filename.substr(
0, output_image_relocation_filename.find_last_of('/'));
@@ -976,6 +1178,26 @@ static int patchoat_image(TimingLogger& timings,
return ret ? EXIT_SUCCESS : EXIT_FAILURE;
}
+static int patchoat_verify_image(TimingLogger& timings,
+ InstructionSet isa,
+ const std::string& input_image_location,
+ const std::string& output_image_directory) {
+ CHECK(!input_image_location.empty());
+ TimingLogger::ScopedTiming pt("verify image and oat", &timings);
+
+ bool ret =
+ PatchOat::Verify(
+ input_image_location,
+ output_image_directory,
+ isa,
+ &timings);
+
+ if (kIsDebugBuild) {
+ LOG(INFO) << "Exiting with return ... " << ret;
+ }
+ return ret ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
static int patchoat(int argc, char **argv) {
InitLogging(argv, Runtime::Abort);
MemMap::Init();
@@ -1003,6 +1225,7 @@ static int patchoat(int argc, char **argv) {
off_t base_delta = 0;
bool base_delta_set = false;
bool dump_timings = kIsDebugBuild;
+ bool verify = false;
for (int i = 0; i < argc; ++i) {
const StringPiece option(argv[i]);
@@ -1034,24 +1257,40 @@ static int patchoat(int argc, char **argv) {
dump_timings = true;
} else if (option == "--no-dump-timings") {
dump_timings = false;
+ } else if (option == "--verify") {
+ verify = true;
} else {
Usage("Unknown argument %s", option.data());
}
}
+ // TODO: Have calls to patchoat pass in the output_image directory instead of
+ // the output_image_filename.
+ std::string output_image_directory;
+ if (!output_image_filename.empty())
+ output_image_directory = android::base::Dirname(output_image_filename);
+
// The instruction set is mandatory. This simplifies things...
if (!isa_set) {
Usage("Instruction set must be set.");
}
- int ret = patchoat_image(timings,
- isa,
- input_image_location,
- output_image_filename,
- output_image_relocation_filename,
- base_delta,
- base_delta_set,
- debug);
+ int ret;
+ if (verify) {
+ ret = patchoat_verify_image(timings,
+ isa,
+ input_image_location,
+ output_image_directory);
+ } else {
+ ret = patchoat_patch_image(timings,
+ isa,
+ input_image_location,
+ output_image_directory,
+ output_image_relocation_filename,
+ base_delta,
+ base_delta_set,
+ debug);
+ }
timings.EndTiming();
if (dump_timings) {
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index 1033a2e5e1..ba59d570a7 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -53,6 +53,10 @@ class PatchOat {
const std::string& output_image_relocation_directory,
InstructionSet isa,
TimingLogger* timings);
+ static bool Verify(const std::string& image_location,
+ const std::string& output_image_filename,
+ InstructionSet isa,
+ TimingLogger* timings);
// Generates a patch which can be used to efficiently relocate the original file or to check that
// a relocated file matches the original. The patch is generated from the difference of the
diff --git a/patchoat/patchoat_test.cc b/patchoat/patchoat_test.cc
index 90cb4f8310..69c6bfae30 100644
--- a/patchoat/patchoat_test.cc
+++ b/patchoat/patchoat_test.cc
@@ -124,18 +124,36 @@ class PatchoatTest : public DexoptTest {
return RunDex2OatOrPatchoat(argv, error_msg);
}
- bool RelocateBootImage(const std::string& input_image_location,
- const std::string& output_image_filename,
- off_t base_offset_delta,
- std::string* error_msg) {
+ static std::vector<std::string> BasePatchoatCommand(const std::string& input_image_location,
+ off_t base_offset_delta) {
Runtime* const runtime = Runtime::Current();
std::vector<std::string> argv;
argv.push_back(runtime->GetPatchoatExecutable());
argv.push_back("--input-image-location=" + input_image_location);
- argv.push_back("--output-image-file=" + output_image_filename);
argv.push_back(StringPrintf("--base-offset-delta=0x%jx", (intmax_t) base_offset_delta));
argv.push_back(StringPrintf("--instruction-set=%s", GetInstructionSetString(kRuntimeISA)));
+ return argv;
+ }
+
+ bool RelocateBootImage(const std::string& input_image_location,
+ const std::string& output_image_filename,
+ off_t base_offset_delta,
+ std::string* error_msg) {
+ std::vector<std::string> argv = BasePatchoatCommand(input_image_location, base_offset_delta);
+ argv.push_back("--output-image-file=" + output_image_filename);
+
+ return RunDex2OatOrPatchoat(argv, error_msg);
+ }
+
+ bool VerifyBootImage(const std::string& input_image_location,
+ const std::string& output_image_filename,
+ off_t base_offset_delta,
+ std::string* error_msg) {
+ std::vector<std::string> argv = BasePatchoatCommand(input_image_location, base_offset_delta);
+ argv.push_back("--output-image-file=" + output_image_filename);
+ argv.push_back("--verify");
+
return RunDex2OatOrPatchoat(argv, error_msg);
}
@@ -143,13 +161,8 @@ class PatchoatTest : public DexoptTest {
const std::string& output_rel_filename,
off_t base_offset_delta,
std::string* error_msg) {
- Runtime* const runtime = Runtime::Current();
- std::vector<std::string> argv;
- argv.push_back(runtime->GetPatchoatExecutable());
- argv.push_back("--input-image-location=" + input_image_location);
+ std::vector<std::string> argv = BasePatchoatCommand(input_image_location, base_offset_delta);
argv.push_back("--output-image-relocation-file=" + output_rel_filename);
- argv.push_back(StringPrintf("--base-offset-delta=0x%jx", (intmax_t) base_offset_delta));
- argv.push_back(StringPrintf("--instruction-set=%s", GetInstructionSetString(kRuntimeISA)));
return RunDex2OatOrPatchoat(argv, error_msg);
}
@@ -280,34 +293,6 @@ class PatchoatTest : public DexoptTest {
}
bool BinaryDiff(
- const std::string& filename1,
- const std::vector<uint8_t>& data1,
- const std::string& filename2,
- const std::vector<uint8_t>& data2,
- std::string* error_msg) {
- if (data1.size() != data1.size()) {
- *error_msg =
- StringPrintf(
- "%s and %s are of different size: %zu vs %zu",
- filename1.c_str(),
- filename2.c_str(),
- data1.size(),
- data2.size());
- return true;
- }
- size_t size = data1.size();
- for (size_t i = 0; i < size; i++) {
- if (data1[i] != data2[i]) {
- *error_msg =
- StringPrintf("%s and %s differ at offset %zu", filename1.c_str(), filename2.c_str(), i);
- return true;
- }
- }
-
- return false;
- }
-
- bool BinaryDiff(
const std::string& filename1, const std::string& filename2, std::string* error_msg) {
std::string read_error_msg;
std::vector<uint8_t> image1;
@@ -320,97 +305,26 @@ class PatchoatTest : public DexoptTest {
*error_msg = StringPrintf("Failed to read %s: %s", filename2.c_str(), read_error_msg.c_str());
return true;
}
- return BinaryDiff(filename1, image1, filename2, image2, error_msg);
- }
-
- bool IsImageIdenticalToOriginalExceptForRelocation(
- const std::string& relocated_filename,
- const std::string& original_filename,
- const std::string& rel_filename,
- std::string* error_msg) {
- *error_msg = "";
- std::string read_error_msg;
- std::vector<uint8_t> rel;
- if (!ReadFully(rel_filename, &rel, &read_error_msg)) {
- *error_msg =
- StringPrintf("Failed to read %s: %s", rel_filename.c_str(), read_error_msg.c_str());
- return false;
- }
- std::vector<uint8_t> relocated;
- if (!ReadFully(relocated_filename, &relocated, &read_error_msg)) {
- *error_msg =
- StringPrintf("Failed to read %s: %s", relocated_filename.c_str(), read_error_msg.c_str());
- return false;
- }
-
- size_t image_size = relocated.size();
- if ((image_size % 4) != 0) {
+ if (image1.size() != image1.size()) {
*error_msg =
StringPrintf(
- "Relocated image file %s size not multiple of 4: %zu",
- relocated_filename.c_str(), image_size);
- return false;
- }
- if (image_size > UINT32_MAX) {
- *error_msg =
- StringPrintf(
- "Relocated image file %s too large: %zu" , relocated_filename.c_str(), image_size);
- return false;
+ "%s and %s are of different size: %zu vs %zu",
+ filename1.c_str(),
+ filename2.c_str(),
+ image1.size(),
+ image2.size());
+ return true;
}
-
- const ImageHeader& relocated_header = *reinterpret_cast<const ImageHeader*>(relocated.data());
- off_t expected_diff = relocated_header.GetPatchDelta();
-
- if (expected_diff != 0) {
- // Relocated image is expected to differ from the original due to relocation.
- // Unrelocate the image in memory to compensate.
- uint8_t* image_start = relocated.data();
- const uint8_t* rel_end = &rel[rel.size()];
- if (rel.size() < SHA256_DIGEST_LENGTH) {
+ size_t size = image1.size();
+ for (size_t i = 0; i < size; i++) {
+ if (image1[i] != image2[i]) {
*error_msg =
- StringPrintf("Malformed image relocation file %s: too short", rel_filename.c_str());
- return false;
- }
- const uint8_t* rel_ptr = &rel[SHA256_DIGEST_LENGTH];
- // The remaining .rel file consists of offsets at which relocation should've occurred.
- // For each offset, we "unrelocate" the image by subtracting the expected relocation
- // diff value (as specified in the image header).
- //
- // Each offset is encoded as a delta/diff relative to the previous offset. With the
- // very first offset being encoded relative to offset 0.
- // Deltas are encoded using little-endian 7 bits per byte encoding, with all bytes except
- // the last one having the highest bit set.
- uint32_t offset = 0;
- while (rel_ptr != rel_end) {
- uint32_t offset_delta = 0;
- if (DecodeUnsignedLeb128Checked(&rel_ptr, rel_end, &offset_delta)) {
- offset += offset_delta;
- uint32_t *image_value = reinterpret_cast<uint32_t*>(image_start + offset);
- *image_value -= expected_diff;
- } else {
- *error_msg =
- StringPrintf(
- "Malformed image relocation file %s: "
- "last byte has it's most significant bit set",
- rel_filename.c_str());
- return false;
- }
+ StringPrintf("%s and %s differ at offset %zu", filename1.c_str(), filename2.c_str(), i);
+ return true;
}
}
- // Image in memory is now supposed to be identical to the original. Compare it to the original.
- std::vector<uint8_t> original;
- if (!ReadFully(original_filename, &original, &read_error_msg)) {
- *error_msg =
- StringPrintf("Failed to read %s: %s", original_filename.c_str(), read_error_msg.c_str());
- return false;
- }
- if (BinaryDiff(relocated_filename, relocated, original_filename, original, error_msg)) {
- return false;
- }
-
- // Relocated image is identical to the original, once relocations are taken into account
- return true;
+ return false;
}
};
@@ -524,7 +438,7 @@ TEST_F(PatchoatTest, PatchoatRelocationSameAsDex2oatRelocation) {
#endif
}
-TEST_F(PatchoatTest, RelFileSufficientToUnpatch) {
+TEST_F(PatchoatTest, RelFileVerification) {
// This test checks that a boot image relocated using patchoat can be unrelocated using the .rel
// file created by patchoat.
@@ -546,10 +460,6 @@ TEST_F(PatchoatTest, RelFileSufficientToUnpatch) {
}
// Generate image relocation file for the original boot image
- ScratchFile rel_scratch;
- rel_scratch.Unlink();
- std::string rel_dir = rel_scratch.GetFilename();
- ASSERT_EQ(0, mkdir(rel_dir.c_str(), 0700));
std::string dex2oat_orig_with_arch_dir =
dex2oat_orig_dir + "/" + GetInstructionSetString(kRuntimeISA);
// The arch-including symlink is needed by patchoat
@@ -557,7 +467,7 @@ TEST_F(PatchoatTest, RelFileSufficientToUnpatch) {
off_t base_addr_delta = 0x100000;
if (!GenerateBootImageRelFile(
dex2oat_orig_dir + "/boot.art",
- rel_dir + "/boot.art.rel",
+ dex2oat_orig_dir + "/boot.art.rel",
base_addr_delta,
&error_msg)) {
FAIL() << "RelocateBootImage failed: " << error_msg;
@@ -582,8 +492,8 @@ TEST_F(PatchoatTest, RelFileSufficientToUnpatch) {
// Assert that patchoat created the same set of .art and .art.rel files
std::vector<std::string> rel_basenames;
std::vector<std::string> relocated_image_basenames;
- if (!ListDirFilesEndingWith(rel_dir, "", &rel_basenames, &error_msg)) {
- FAIL() << "Failed to list *.art.rel files in " << rel_dir << ": " << error_msg;
+ if (!ListDirFilesEndingWith(dex2oat_orig_dir, ".rel", &rel_basenames, &error_msg)) {
+ FAIL() << "Failed to list *.art.rel files in " << dex2oat_orig_dir << ": " << error_msg;
}
if (!ListDirFilesEndingWith(relocated_dir, ".art", &relocated_image_basenames, &error_msg)) {
FAIL() << "Failed to list *.art files in " << relocated_dir << ": " << error_msg;
@@ -611,52 +521,19 @@ TEST_F(PatchoatTest, RelFileSufficientToUnpatch) {
}
ASSERT_EQ(rel_shortened_basenames, relocated_image_shortened_basenames);
- // For each image file, assert that unrelocating the image produces its original version
- for (size_t i = 0; i < relocated_image_basenames.size(); i++) {
- const std::string& original_image_filename =
- dex2oat_orig_dir + "/" + relocated_image_shortened_basenames[i] + ".art";
- const std::string& relocated_image_filename =
- relocated_dir + "/" + relocated_image_basenames[i];
- const std::string& rel_filename = rel_dir + "/" + rel_basenames[i];
-
- // Assert that relocated image differs from the original
- if (!BinaryDiff(original_image_filename, relocated_image_filename, &error_msg)) {
- FAIL() << "Relocated image " << relocated_image_filename
- << " identical to the original image " << original_image_filename;
- }
-
- // Assert that relocated image is identical to the original except for relocations described in
- // the .rel file
- if (!IsImageIdenticalToOriginalExceptForRelocation(
- relocated_image_filename, original_image_filename, rel_filename, &error_msg)) {
- FAIL() << "Unrelocating " << relocated_image_filename << " using " << rel_filename
- << " did not produce the same output as " << original_image_filename << ": " << error_msg;
- }
-
- // Assert that the digest of original image in .rel file is as expected
- std::vector<uint8_t> original;
- if (!ReadFully(original_image_filename, &original, &error_msg)) {
- FAIL() << "Failed to read original image " << original_image_filename;
- }
- std::vector<uint8_t> rel;
- if (!ReadFully(rel_filename, &rel, &error_msg)) {
- FAIL() << "Failed to read image relocation file " << rel_filename;
- }
- uint8_t original_image_digest[SHA256_DIGEST_LENGTH];
- SHA256(original.data(), original.size(), original_image_digest);
- const uint8_t* original_image_digest_in_rel_file = rel.data();
- if (memcmp(original_image_digest_in_rel_file, original_image_digest, SHA256_DIGEST_LENGTH)) {
- FAIL() << "Digest of original image in " << rel_filename << " does not match the original"
- " image " << original_image_filename;
- }
+ // Assert that verification works with the .rel files.
+ if (!VerifyBootImage(
+ dex2oat_orig_dir + "/boot.art",
+ relocated_dir + "/boot.art",
+ base_addr_delta,
+ &error_msg)) {
+ FAIL() << "VerifyBootImage failed: " << error_msg;
}
ClearDirectory(dex2oat_orig_dir.c_str(), /*recursive*/ true);
- ClearDirectory(rel_dir.c_str(), /*recursive*/ true);
ClearDirectory(relocated_dir.c_str(), /*recursive*/ true);
rmdir(dex2oat_orig_dir.c_str());
- rmdir(rel_dir.c_str());
rmdir(relocated_dir.c_str());
}
diff --git a/profman/Android.bp b/profman/Android.bp
index ea682b40a0..6592b9dec0 100644
--- a/profman/Android.bp
+++ b/profman/Android.bp
@@ -31,6 +31,7 @@ cc_defaults {
},
shared_libs: [
+ "libdexfile",
"libbase",
],
}
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index 642d26e8f5..c75f3e9688 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -209,7 +209,8 @@ class ProfileAssistantTest : public CommonRuntimeTest {
bool CreateProfile(const std::string& profile_file_contents,
const std::string& filename,
- const std::string& dex_location) {
+ const std::string& dex_location,
+ bool skip_verification) {
ScratchFile class_names_file;
File* file = class_names_file.GetFile();
EXPECT_TRUE(file->WriteFully(profile_file_contents.c_str(), profile_file_contents.length()));
@@ -222,6 +223,9 @@ class ProfileAssistantTest : public CommonRuntimeTest {
argv_str.push_back("--reference-profile-file=" + filename);
argv_str.push_back("--apk=" + dex_location);
argv_str.push_back("--dex-location=" + dex_location);
+ if (skip_verification) {
+ argv_str.push_back("--skip-apk-verification");
+ }
std::string error;
EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0);
return true;
@@ -238,6 +242,7 @@ class ProfileAssistantTest : public CommonRuntimeTest {
argv_str.push_back("--profile-file=" + filename);
argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]);
argv_str.push_back("--dex-location=" + GetLibCoreDexFileNames()[0]);
+ argv_str.push_back("--skip-apk-verification");
argv_str.push_back("--dump-output-to-fd=" + std::to_string(GetFd(output_file)));
std::string error;
EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0);
@@ -268,7 +273,8 @@ class ProfileAssistantTest : public CommonRuntimeTest {
ScratchFile profile_file;
EXPECT_TRUE(CreateProfile(input_file_contents,
profile_file.GetFilename(),
- GetLibCoreDexFileNames()[0]));
+ GetLibCoreDexFileNames()[0],
+ /* skip_verification */ true));
profile_file.GetFile()->ResetOffset();
EXPECT_TRUE(DumpClassesAndMethods(profile_file.GetFilename(), output_file_contents));
return true;
@@ -675,7 +681,8 @@ TEST_F(ProfileAssistantTest, TestProfileCreationGenerateMethods) {
ScratchFile profile_file;
EXPECT_TRUE(CreateProfile(input_file_contents,
profile_file.GetFilename(),
- GetLibCoreDexFileNames()[0]));
+ GetLibCoreDexFileNames()[0],
+ /* skip_verification */ true));
ProfileCompilationInfo info;
profile_file.GetFile()->ResetOffset();
ASSERT_TRUE(info.Load(GetFd(profile_file)));
@@ -731,7 +738,8 @@ TEST_F(ProfileAssistantTest, TestBootImageProfile) {
"H" + kHotMethod + "\n" +
kUncommonDirtyClass;
profiles.emplace_back(ScratchFile());
- EXPECT_TRUE(CreateProfile(dex1, profiles.back().GetFilename(), core_dex));
+ EXPECT_TRUE(CreateProfile(
+ dex1, profiles.back().GetFilename(), core_dex, /* skip_verification */ true));
// Create a bunch of boot profiles.
std::string dex2 =
@@ -741,7 +749,8 @@ TEST_F(ProfileAssistantTest, TestBootImageProfile) {
"P" + kMultiMethod + "\n" +
kUncommonDirtyClass;
profiles.emplace_back(ScratchFile());
- EXPECT_TRUE(CreateProfile(dex2, profiles.back().GetFilename(), core_dex));
+ EXPECT_TRUE(CreateProfile(
+ dex2, profiles.back().GetFilename(), core_dex, /* skip_verification */ true));
// Create a bunch of boot profiles.
std::string dex3 =
@@ -750,7 +759,8 @@ TEST_F(ProfileAssistantTest, TestBootImageProfile) {
"P" + kMultiMethod + "\n" +
kDirtyClass + "\n";
profiles.emplace_back(ScratchFile());
- EXPECT_TRUE(CreateProfile(dex3, profiles.back().GetFilename(), core_dex));
+ EXPECT_TRUE(CreateProfile(
+ dex3, profiles.back().GetFilename(), core_dex, /* skip_verification */ true));
// Generate the boot profile.
ScratchFile out_profile;
@@ -763,6 +773,7 @@ TEST_F(ProfileAssistantTest, TestBootImageProfile) {
args.push_back("--reference-profile-file=" + out_profile.GetFilename());
args.push_back("--apk=" + core_dex);
args.push_back("--dex-location=" + core_dex);
+ args.push_back("--skip-apk-verification");
for (const ScratchFile& profile : profiles) {
args.push_back("--profile-file=" + profile.GetFilename());
}
@@ -858,7 +869,8 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) {
ScratchFile profile_file;
ASSERT_TRUE(CreateProfile(input_file_contents,
profile_file.GetFilename(),
- GetTestDexFileName("ProfileTestMultiDex")));
+ GetTestDexFileName("ProfileTestMultiDex"),
+ /* skip_verification */ false));
// Load the profile from disk.
ProfileCompilationInfo info;
@@ -1008,7 +1020,8 @@ TEST_F(ProfileAssistantTest, TestProfileCreateWithInvalidData) {
std::string dex_filename = GetTestDexFileName("ProfileTestMultiDex");
ASSERT_TRUE(CreateProfile(input_file_contents,
profile_file.GetFilename(),
- dex_filename));
+ dex_filename,
+ /* skip_verification */ false));
// Load the profile from disk.
ProfileCompilationInfo info;
diff --git a/profman/profman.cc b/profman/profman.cc
index ffc3c0170f..ea6c382a81 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -137,6 +137,7 @@ NO_RETURN static void Usage(const char *fmt, ...) {
UsageError(" --apk-fd=<number>: file descriptor containing an open APK to");
UsageError(" search for dex files");
UsageError(" --apk-=<filename>: an APK to search for dex files");
+ UsageError(" --skip-apk-verification: do not attempt to verify APKs");
UsageError("");
UsageError(" --generate-boot-image-profile: Generate a boot image profile based on input");
UsageError(" profiles. Requires passing in dex files to inspect properties of classes.");
@@ -185,6 +186,7 @@ class ProfMan FINAL {
dump_only_(false),
dump_classes_and_methods_(false),
generate_boot_image_profile_(false),
+ skip_apk_verification_(false),
dump_output_to_fd_(kInvalidFd),
test_profile_num_dex_(kDefaultTestProfileNumDex),
test_profile_method_percerntage_(kDefaultTestProfileMethodPercentage),
@@ -227,6 +229,8 @@ class ProfMan FINAL {
ParseUintOption(option, "--dump-output-to-fd", &dump_output_to_fd_, Usage);
} else if (option == "--generate-boot-image-profile") {
generate_boot_image_profile_ = true;
+ } else if (option == "--skip-apk-verification") {
+ skip_apk_verification_ = true;
} else if (option.starts_with("--boot-image-class-threshold=")) {
ParseUintOption(option,
"--boot-image-class-threshold",
@@ -321,6 +325,10 @@ class ProfMan FINAL {
return result;
}
+ bool ShouldSkipApkVerification() const {
+ return skip_apk_verification_;
+ }
+
void OpenApkFilesFromLocations(std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
bool use_apk_fd_list = !apks_fd_.empty();
if (use_apk_fd_list) {
@@ -342,7 +350,7 @@ class ProfMan FINAL {
if (use_apk_fd_list) {
if (dex_file_loader.OpenZip(apks_fd_[i],
dex_locations_[i],
- /* verify */ true,
+ /* verify */ !ShouldSkipApkVerification(),
kVerifyChecksum,
&error_msg,
&dex_files_for_location)) {
@@ -353,7 +361,7 @@ class ProfMan FINAL {
} else {
if (dex_file_loader.Open(apk_files_[i].c_str(),
dex_locations_[i],
- /* verify */ true,
+ /* verify */ !ShouldSkipApkVerification(),
kVerifyChecksum,
&error_msg,
&dex_files_for_location)) {
@@ -1148,6 +1156,7 @@ class ProfMan FINAL {
bool dump_only_;
bool dump_classes_and_methods_;
bool generate_boot_image_profile_;
+ bool skip_apk_verification_;
int dump_output_to_fd_;
BootImageOptions boot_image_options_;
std::string test_profile_;
diff --git a/runtime/Android.bp b/runtime/Android.bp
index f2f7c3e3d0..db9bceaf29 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -21,6 +21,7 @@ cc_defaults {
srcs: [
"dex/compact_dex_debug_info.cc",
"dex/compact_dex_file.cc",
+ "dex/descriptors_names.cc",
"dex/dex_file.cc",
"dex/dex_file_exception_helpers.cc",
"dex/dex_file_loader.cc",
@@ -29,8 +30,7 @@ cc_defaults {
"dex/dex_instruction.cc",
"dex/modifiers.cc",
"dex/standard_dex_file.cc",
- "utf.cc",
- "utils.cc",
+ "dex/utf.cc",
],
target: {
@@ -51,7 +51,7 @@ cc_defaults {
],
},
},
- generated_sources: ["art_operator_srcs"],
+ generated_sources: ["dexfile_operator_srcs"],
include_dirs: [
"external/zlib",
],
@@ -133,19 +133,9 @@ cc_defaults {
"common_throws.cc",
"compiler_filter.cc",
"debugger.cc",
- "dex/compact_dex_debug_info.cc",
- "dex/compact_dex_file.cc",
- "dex/dex_file.cc",
"dex/dex_file_annotations.cc",
- "dex/dex_file_exception_helpers.cc",
"dex/dex_file_layout.cc",
- "dex/dex_file_loader.cc",
"dex/art_dex_file_loader.cc",
- "dex/dex_file_tracking_registrar.cc",
- "dex/dex_file_verifier.cc",
- "dex/dex_instruction.cc",
- "dex/modifiers.cc",
- "dex/standard_dex_file.cc",
"dex_to_dex_decompiler.cc",
"elf_file.cc",
"exec_utils.cc",
@@ -302,7 +292,6 @@ cc_defaults {
"trace.cc",
"transaction.cc",
"type_lookup_table.cc",
- "utf.cc",
"utils.cc",
"vdex_file.cc",
"verifier/instruction_flags.cc",
@@ -498,6 +487,7 @@ cc_defaults {
"jni_platform_headers",
],
shared_libs: [
+ "libdexfile",
"libnativebridge",
"libnativeloader",
"libbacktrace",
@@ -534,11 +524,7 @@ gensrcs {
"debugger.h",
"base/unix_file/fd_file.h",
"class_status.h",
- "dex/dex_file.h",
"dex/dex_file_layout.h",
- "dex/dex_instruction.h",
- "dex/dex_instruction_utils.h",
- "dex/invoke_type.h",
"gc_root.h",
"gc/allocator_type.h",
"gc/allocator/rosalloc.h",
@@ -657,6 +643,7 @@ art_cc_test {
"dex/dex_file_test.cc",
"dex/dex_file_verifier_test.cc",
"dex/dex_instruction_test.cc",
+ "dex/utf_test.cc",
"entrypoints/math_entrypoints_test.cc",
"entrypoints/quick/quick_trampoline_entrypoints_test.cc",
"entrypoints_order_test.cc",
@@ -691,6 +678,7 @@ art_cc_test {
"leb128_test.cc",
"mem_map_test.cc",
"memory_region_test.cc",
+ "method_handles_test.cc",
"mirror/dex_cache_test.cc",
"mirror/method_type_test.cc",
"mirror/object_test.cc",
@@ -701,6 +689,7 @@ art_cc_test {
"oat_file_assistant_test.cc",
"parsed_options_test.cc",
"prebuilt_tools_test.cc",
+ "primitive_test.cc",
"reference_table_test.cc",
"runtime_callbacks_test.cc",
"subtype_check_info_test.cc",
@@ -708,7 +697,6 @@ art_cc_test {
"thread_pool_test.cc",
"transaction_test.cc",
"type_lookup_table_test.cc",
- "utf_test.cc",
"utils_test.cc",
"vdex_file_test.cc",
"verifier/method_verifier_test.cc",
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 65bacd8237..e6e35c89c9 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -377,6 +377,22 @@ inline bool ArtMethod::HasSingleImplementation() {
return (GetAccessFlags() & kAccSingleImplementation) != 0;
}
+inline bool ArtMethod::IsHiddenIntrinsic(uint32_t ordinal) {
+ switch (static_cast<Intrinsics>(ordinal)) {
+ case Intrinsics::kReferenceGetReferent:
+ case Intrinsics::kSystemArrayCopyChar:
+ case Intrinsics::kStringGetCharsNoCheck:
+ case Intrinsics::kVarHandleFullFence:
+ case Intrinsics::kVarHandleAcquireFence:
+ case Intrinsics::kVarHandleReleaseFence:
+ case Intrinsics::kVarHandleLoadLoadFence:
+ case Intrinsics::kVarHandleStoreStoreFence:
+ return true;
+ default:
+ return false;
+ }
+}
+
inline void ArtMethod::SetIntrinsic(uint32_t intrinsic) {
// Currently we only do intrinsics for static/final methods or methods of final
// classes. We don't set kHasSingleImplementation for those methods.
@@ -413,10 +429,14 @@ inline void ArtMethod::SetIntrinsic(uint32_t intrinsic) {
DCHECK_EQ(is_default_conflict, IsDefaultConflicting());
DCHECK_EQ(is_compilable, IsCompilable());
DCHECK_EQ(must_count_locks, MustCountLocks());
- // We need to special case java.lang.ref.Reference.getRefererent. The Java method
- // is hidden but we do not yet have a way of making intrinsics hidden.
- if (intrinsic != static_cast<uint32_t>(Intrinsics::kReferenceGetReferent)) {
- DCHECK_EQ(hidden_api_list, GetHiddenApiAccessFlags());
+ if (kIsDebugBuild) {
+ if (IsHiddenIntrinsic(intrinsic)) {
+ // Special case some of our intrinsics because the access flags clash
+ // with the intrinsics ordinal.
+ DCHECK_EQ(HiddenApiAccessFlags::kWhitelist, GetHiddenApiAccessFlags());
+ } else {
+ DCHECK_EQ(hidden_api_list, GetHiddenApiAccessFlags());
+ }
}
} else {
SetAccessFlags(new_value);
@@ -466,7 +486,15 @@ inline void ArtMethod::UpdateEntrypoints(const Visitor& visitor, PointerSize poi
}
inline CodeItemInstructionAccessor ArtMethod::DexInstructions() {
- return CodeItemInstructionAccessor(this);
+ return CodeItemInstructionAccessor(*GetDexFile(), GetCodeItem());
+}
+
+inline CodeItemDataAccessor ArtMethod::DexInstructionData() {
+ return CodeItemDataAccessor(*GetDexFile(), GetCodeItem());
+}
+
+inline CodeItemDebugInfoAccessor ArtMethod::DexInstructionDebugInfo() {
+ return CodeItemDebugInfoAccessor(*GetDexFile(), GetCodeItem(), GetDexMethodIndex());
}
} // namespace art
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 96468bba60..efdf5991ec 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -272,7 +272,7 @@ uint32_t ArtMethod::FindCatchBlock(Handle<mirror::Class> exception_type,
// Default to handler not found.
uint32_t found_dex_pc = dex::kDexNoIndex;
// Iterate over the catch handlers associated with dex_pc.
- CodeItemDataAccessor accessor(this);
+ CodeItemDataAccessor accessor(DexInstructionData());
for (CatchHandlerIterator it(accessor, dex_pc); it.HasNext(); it.Next()) {
dex::TypeIndex iter_type_idx = it.GetHandlerTypeIndex();
// Catch all case
diff --git a/runtime/art_method.h b/runtime/art_method.h
index ce8e8ac612..cec2ec4df2 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -667,6 +667,10 @@ class ArtMethod FINAL {
return hotness_count_;
}
+ static MemberOffset HotnessCountOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(ArtMethod, hotness_count_));
+ }
+
ArrayRef<const uint8_t> GetQuickenedInfo() REQUIRES_SHARED(Locks::mutator_lock_);
// Returns the method header for the compiled code containing 'pc'. Note that runtime
@@ -727,6 +731,14 @@ class ArtMethod FINAL {
ALWAYS_INLINE CodeItemInstructionAccessor DexInstructions()
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Returns the dex code item data section of the DexFile for the art method.
+ ALWAYS_INLINE CodeItemDataAccessor DexInstructionData()
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Returns the dex code item debug info section of the DexFile for the art method.
+ ALWAYS_INLINE CodeItemDebugInfoAccessor DexInstructionDebugInfo()
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
protected:
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
// The class we are a part of.
@@ -753,7 +765,7 @@ class ArtMethod FINAL {
// ifTable.
uint16_t method_index_;
- // The hotness we measure for this method. Managed by the interpreter. Not atomic, as we allow
+ // The hotness we measure for this method. Not atomic, as we allow
// missing increments: if the method is hot, we will see it eventually.
uint16_t hotness_count_;
@@ -846,6 +858,9 @@ class ArtMethod FINAL {
} while (!access_flags_.compare_exchange_weak(old_access_flags, new_access_flags));
}
+ // Returns true if the given intrinsic is considered hidden.
+ bool IsHiddenIntrinsic(uint32_t ordinal);
+
DISALLOW_COPY_AND_ASSIGN(ArtMethod); // Need to use CopyFrom to deal with 32 vs 64 bits.
};
diff --git a/runtime/base/file_utils.cc b/runtime/base/file_utils.cc
index d22fd994ee..58990f344b 100644
--- a/runtime/base/file_utils.cc
+++ b/runtime/base/file_utils.cc
@@ -50,10 +50,10 @@
#include "dex/dex_file-inl.h"
#include "dex/dex_file_loader.h"
#include "dex/dex_instruction.h"
+#include "dex/utf-inl.h"
#include "oat_quick_method_header.h"
#include "os.h"
#include "scoped_thread_state_change-inl.h"
-#include "utf-inl.h"
#if defined(__APPLE__)
#include <crt_externs.h>
diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h
index 0c29e257a1..e2ad7fd83f 100644
--- a/runtime/check_reference_map_visitor.h
+++ b/runtime/check_reference_map_visitor.h
@@ -67,7 +67,7 @@ class CheckReferenceMapVisitor : public StackVisitor {
CodeInfo code_info = GetCurrentOatQuickMethodHeader()->GetOptimizedCodeInfo();
CodeInfoEncoding encoding = code_info.ExtractEncoding();
StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
- CodeItemDataAccessor accessor(m);
+ CodeItemDataAccessor accessor(m->DexInstructionData());
uint16_t number_of_dex_registers = accessor.RegistersSize();
DexRegisterMap dex_register_map =
code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index af45a69bd5..32d304073c 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -53,6 +53,7 @@
#include "dex/dex_file-inl.h"
#include "dex/dex_file_exception_helpers.h"
#include "dex/dex_file_loader.h"
+#include "dex/utf.h"
#include "entrypoints/entrypoint_utils.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "experimental_flags.h"
@@ -115,7 +116,6 @@
#include "thread-inl.h"
#include "thread_list.h"
#include "trace.h"
-#include "utf.h"
#include "utils.h"
#include "utils/dex_cache_arrays_layout-inl.h"
#include "verifier/method_verifier.h"
@@ -4329,7 +4329,7 @@ void ClassLinker::ResolveClassExceptionHandlerTypes(Handle<mirror::Class> klass)
void ClassLinker::ResolveMethodExceptionHandlerTypes(ArtMethod* method) {
// similar to DexVerifier::ScanTryCatchBlocks and dex2oat's ResolveExceptionsForMethod.
- CodeItemDataAccessor accessor(method);
+ CodeItemDataAccessor accessor(method->DexInstructionData());
if (!accessor.HasCodeItem()) {
return; // native or abstract method
}
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 03774f45cd..19e7f7686d 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -551,7 +551,7 @@ static bool IsValidImplicitCheck(uintptr_t addr, const Instruction& instr)
void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) {
uint32_t throw_dex_pc;
ArtMethod* method = Thread::Current()->GetCurrentMethod(&throw_dex_pc);
- CodeItemInstructionAccessor accessor(method);
+ CodeItemInstructionAccessor accessor(method->DexInstructions());
CHECK_LT(throw_dex_pc, accessor.InsnsSizeInCodeUnits());
const Instruction& instr = accessor.InstructionAt(throw_dex_pc);
if (check_address && !IsValidImplicitCheck(addr, instr)) {
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 842cd7330c..61ad725b79 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -37,6 +37,7 @@
#include "dex/dex_file_annotations.h"
#include "dex/dex_file_types.h"
#include "dex/dex_instruction.h"
+#include "dex/utf.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/allocation_record.h"
@@ -66,7 +67,6 @@
#include "scoped_thread_state_change-inl.h"
#include "stack.h"
#include "thread_list.h"
-#include "utf.h"
#include "well_known_classes.h"
namespace art {
@@ -1533,7 +1533,7 @@ static uint32_t MangleAccessFlags(uint32_t accessFlags) {
*/
static uint16_t MangleSlot(uint16_t slot, ArtMethod* m)
REQUIRES_SHARED(Locks::mutator_lock_) {
- CodeItemDataAccessor accessor(m);
+ CodeItemDataAccessor accessor(m->DexInstructionData());
if (!accessor.HasCodeItem()) {
// We should not get here for a method without code (native, proxy or abstract). Log it and
// return the slot as is since all registers are arguments.
@@ -1564,7 +1564,7 @@ static size_t GetMethodNumArgRegistersIncludingThis(ArtMethod* method)
*/
static uint16_t DemangleSlot(uint16_t slot, ArtMethod* m, JDWP::JdwpError* error)
REQUIRES_SHARED(Locks::mutator_lock_) {
- CodeItemDataAccessor accessor(m);
+ CodeItemDataAccessor accessor(m->DexInstructionData());
if (!accessor.HasCodeItem()) {
// We should not get here for a method without code (native, proxy or abstract). Log it and
// return the slot as is since all registers are arguments.
@@ -1675,7 +1675,7 @@ void Dbg::OutputLineTable(JDWP::RefTypeId, JDWP::MethodId method_id, JDWP::Expan
}
};
ArtMethod* m = FromMethodId(method_id);
- CodeItemDebugInfoAccessor accessor(m);
+ CodeItemDebugInfoAccessor accessor(m->DexInstructionDebugInfo());
uint64_t start, end;
if (!accessor.HasCodeItem()) {
DCHECK(m->IsNative() || m->IsProxyMethod());
@@ -1741,7 +1741,7 @@ void Dbg::OutputVariableTable(JDWP::RefTypeId, JDWP::MethodId method_id, bool wi
}
};
ArtMethod* m = FromMethodId(method_id);
- CodeItemDebugInfoAccessor accessor(m);
+ CodeItemDebugInfoAccessor accessor(m->DexInstructionDebugInfo());
// arg_count considers doubles and longs to take 2 units.
// variable_count considers everything to take 1 unit.
@@ -1791,7 +1791,7 @@ JDWP::JdwpError Dbg::GetBytecodes(JDWP::RefTypeId, JDWP::MethodId method_id,
if (m == nullptr) {
return JDWP::ERR_INVALID_METHODID;
}
- CodeItemDataAccessor accessor(m);
+ CodeItemDataAccessor accessor(m->DexInstructionData());
size_t byte_count = accessor.InsnsSizeInCodeUnits() * 2;
const uint8_t* begin = reinterpret_cast<const uint8_t*>(accessor.Insns());
const uint8_t* end = begin + byte_count;
@@ -3908,7 +3908,7 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize
// Note: if the thread is not running Java code (pure native thread), there is no "current"
// method on the stack (and no line number either).
if (m != nullptr && !m->IsNative()) {
- CodeItemDebugInfoAccessor accessor(m);
+ CodeItemDebugInfoAccessor accessor(m->DexInstructionDebugInfo());
DebugCallbackContext context(single_step_control, line_number, accessor.InsnsSizeInCodeUnits());
m->GetDexFile()->DecodeDebugPositionInfo(accessor.DebugInfoOffset(),
DebugCallbackContext::Callback,
diff --git a/runtime/dex/code_item_accessors-inl.h b/runtime/dex/code_item_accessors-inl.h
index 63fd120991..9c39935d3b 100644
--- a/runtime/dex/code_item_accessors-inl.h
+++ b/runtime/dex/code_item_accessors-inl.h
@@ -17,26 +17,187 @@
#ifndef ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_INL_H_
#define ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_INL_H_
-#include "code_item_accessors-no_art-inl.h"
+#include "code_item_accessors.h"
-#include "art_method-inl.h"
#include "compact_dex_file.h"
#include "dex_file-inl.h"
-#include "oat_file.h"
#include "standard_dex_file.h"
+// The no ART version is used by binaries that don't include the whole runtime.
namespace art {
-inline CodeItemInstructionAccessor::CodeItemInstructionAccessor(ArtMethod* method)
- : CodeItemInstructionAccessor(*method->GetDexFile(), method->GetCodeItem()) {}
+inline void CodeItemInstructionAccessor::Init(uint32_t insns_size_in_code_units,
+ const uint16_t* insns) {
+ insns_size_in_code_units_ = insns_size_in_code_units;
+ insns_ = insns;
+}
-inline CodeItemDataAccessor::CodeItemDataAccessor(ArtMethod* method)
- : CodeItemDataAccessor(*method->GetDexFile(), method->GetCodeItem()) {}
+inline void CodeItemInstructionAccessor::Init(const CompactDexFile::CodeItem& code_item) {
+ uint32_t insns_size_in_code_units;
+ code_item.DecodeFields</*kDecodeOnlyInstructionCount*/ true>(
+ &insns_size_in_code_units,
+ /*registers_size*/ nullptr,
+ /*ins_size*/ nullptr,
+ /*outs_size*/ nullptr,
+ /*tries_size*/ nullptr);
+ Init(insns_size_in_code_units, code_item.insns_);
+}
-inline CodeItemDebugInfoAccessor::CodeItemDebugInfoAccessor(ArtMethod* method)
- : CodeItemDebugInfoAccessor(*method->GetDexFile(),
- method->GetCodeItem(),
- method->GetDexMethodIndex()) {}
+inline void CodeItemInstructionAccessor::Init(const StandardDexFile::CodeItem& code_item) {
+ Init(code_item.insns_size_in_code_units_, code_item.insns_);
+}
+
+inline void CodeItemInstructionAccessor::Init(const DexFile& dex_file,
+ const DexFile::CodeItem* code_item) {
+ if (code_item != nullptr) {
+ DCHECK(dex_file.IsInDataSection(code_item));
+ if (dex_file.IsCompactDexFile()) {
+ Init(down_cast<const CompactDexFile::CodeItem&>(*code_item));
+ } else {
+ DCHECK(dex_file.IsStandardDexFile());
+ Init(down_cast<const StandardDexFile::CodeItem&>(*code_item));
+ }
+ }
+}
+
+inline CodeItemInstructionAccessor::CodeItemInstructionAccessor(
+ const DexFile& dex_file,
+ const DexFile::CodeItem* code_item) {
+ Init(dex_file, code_item);
+}
+
+inline DexInstructionIterator CodeItemInstructionAccessor::begin() const {
+ return DexInstructionIterator(insns_, 0u);
+}
+
+inline DexInstructionIterator CodeItemInstructionAccessor::end() const {
+ return DexInstructionIterator(insns_, insns_size_in_code_units_);
+}
+
+inline IterationRange<DexInstructionIterator> CodeItemInstructionAccessor::InstructionsFrom(
+ uint32_t start_dex_pc) const {
+ DCHECK_LT(start_dex_pc, InsnsSizeInCodeUnits());
+ return {
+ DexInstructionIterator(insns_, start_dex_pc),
+ DexInstructionIterator(insns_, insns_size_in_code_units_) };
+}
+
+inline void CodeItemDataAccessor::Init(const CompactDexFile::CodeItem& code_item) {
+ uint32_t insns_size_in_code_units;
+ code_item.DecodeFields</*kDecodeOnlyInstructionCount*/ false>(&insns_size_in_code_units,
+ &registers_size_,
+ &ins_size_,
+ &outs_size_,
+ &tries_size_);
+ CodeItemInstructionAccessor::Init(insns_size_in_code_units, code_item.insns_);
+}
+
+inline void CodeItemDataAccessor::Init(const StandardDexFile::CodeItem& code_item) {
+ CodeItemInstructionAccessor::Init(code_item);
+ registers_size_ = code_item.registers_size_;
+ ins_size_ = code_item.ins_size_;
+ outs_size_ = code_item.outs_size_;
+ tries_size_ = code_item.tries_size_;
+}
+
+inline void CodeItemDataAccessor::Init(const DexFile& dex_file,
+ const DexFile::CodeItem* code_item) {
+ if (code_item != nullptr) {
+ if (dex_file.IsCompactDexFile()) {
+ CodeItemDataAccessor::Init(down_cast<const CompactDexFile::CodeItem&>(*code_item));
+ } else {
+ DCHECK(dex_file.IsStandardDexFile());
+ CodeItemDataAccessor::Init(down_cast<const StandardDexFile::CodeItem&>(*code_item));
+ }
+ }
+}
+
+inline CodeItemDataAccessor::CodeItemDataAccessor(const DexFile& dex_file,
+ const DexFile::CodeItem* code_item) {
+ Init(dex_file, code_item);
+}
+
+inline IterationRange<const DexFile::TryItem*> CodeItemDataAccessor::TryItems() const {
+ const DexFile::TryItem* try_items = DexFile::GetTryItems(end(), 0u);
+ return {
+ try_items,
+ try_items + TriesSize() };
+}
+
+inline const uint8_t* CodeItemDataAccessor::GetCatchHandlerData(size_t offset) const {
+ return DexFile::GetCatchHandlerData(end(), TriesSize(), offset);
+}
+
+inline const DexFile::TryItem* CodeItemDataAccessor::FindTryItem(uint32_t try_dex_pc) const {
+ IterationRange<const DexFile::TryItem*> try_items(TryItems());
+ int32_t index = DexFile::FindTryItem(try_items.begin(),
+ try_items.end() - try_items.begin(),
+ try_dex_pc);
+ return index != -1 ? &try_items.begin()[index] : nullptr;
+}
+
+inline const void* CodeItemDataAccessor::CodeItemDataEnd() const {
+ const uint8_t* handler_data = GetCatchHandlerData();
+
+ if (TriesSize() == 0 || handler_data == nullptr) {
+ return &end().Inst();
+ }
+ // Get the start of the handler data.
+ const uint32_t handlers_size = DecodeUnsignedLeb128(&handler_data);
+ // Manually read each handler.
+ for (uint32_t i = 0; i < handlers_size; ++i) {
+ int32_t uleb128_count = DecodeSignedLeb128(&handler_data) * 2;
+ if (uleb128_count <= 0) {
+ uleb128_count = -uleb128_count + 1;
+ }
+ for (int32_t j = 0; j < uleb128_count; ++j) {
+ DecodeUnsignedLeb128(&handler_data);
+ }
+ }
+ return reinterpret_cast<const void*>(handler_data);
+}
+
+inline void CodeItemDebugInfoAccessor::Init(const DexFile& dex_file,
+ const DexFile::CodeItem* code_item,
+ uint32_t dex_method_index) {
+ if (code_item == nullptr) {
+ return;
+ }
+ dex_file_ = &dex_file;
+ if (dex_file.IsCompactDexFile()) {
+ Init(down_cast<const CompactDexFile::CodeItem&>(*code_item), dex_method_index);
+ } else {
+ DCHECK(dex_file.IsStandardDexFile());
+ Init(down_cast<const StandardDexFile::CodeItem&>(*code_item));
+ }
+}
+
+inline void CodeItemDebugInfoAccessor::Init(const CompactDexFile::CodeItem& code_item,
+ uint32_t dex_method_index) {
+ debug_info_offset_ = down_cast<const CompactDexFile*>(dex_file_)->GetDebugInfoOffset(
+ dex_method_index);
+ CodeItemDataAccessor::Init(code_item);
+}
+
+inline void CodeItemDebugInfoAccessor::Init(const StandardDexFile::CodeItem& code_item) {
+ debug_info_offset_ = code_item.debug_info_off_;
+ CodeItemDataAccessor::Init(code_item);
+}
+
+template<typename NewLocalCallback>
+inline bool CodeItemDebugInfoAccessor::DecodeDebugLocalInfo(bool is_static,
+ uint32_t method_idx,
+ NewLocalCallback new_local,
+ void* context) const {
+ return dex_file_->DecodeDebugLocalInfo(RegistersSize(),
+ InsSize(),
+ InsnsSizeInCodeUnits(),
+ DebugInfoOffset(),
+ is_static,
+ method_idx,
+ new_local,
+ context);
+}
} // namespace art
diff --git a/runtime/dex/code_item_accessors-no_art-inl.h b/runtime/dex/code_item_accessors-no_art-inl.h
index a243a4a6f4..8082be3818 100644
--- a/runtime/dex/code_item_accessors-no_art-inl.h
+++ b/runtime/dex/code_item_accessors-no_art-inl.h
@@ -17,188 +17,7 @@
#ifndef ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_NO_ART_INL_H_
#define ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_NO_ART_INL_H_
-#include "code_item_accessors.h"
-
-#include "compact_dex_file.h"
-#include "dex_file-inl.h"
-#include "standard_dex_file.h"
-
-// The no ART version is used by binaries that don't include the whole runtime.
-namespace art {
-
-inline void CodeItemInstructionAccessor::Init(uint32_t insns_size_in_code_units,
- const uint16_t* insns) {
- insns_size_in_code_units_ = insns_size_in_code_units;
- insns_ = insns;
-}
-
-inline void CodeItemInstructionAccessor::Init(const CompactDexFile::CodeItem& code_item) {
- uint32_t insns_size_in_code_units;
- code_item.DecodeFields</*kDecodeOnlyInstructionCount*/ true>(
- &insns_size_in_code_units,
- /*registers_size*/ nullptr,
- /*ins_size*/ nullptr,
- /*outs_size*/ nullptr,
- /*tries_size*/ nullptr);
- Init(insns_size_in_code_units, code_item.insns_);
-}
-
-inline void CodeItemInstructionAccessor::Init(const StandardDexFile::CodeItem& code_item) {
- Init(code_item.insns_size_in_code_units_, code_item.insns_);
-}
-
-inline void CodeItemInstructionAccessor::Init(const DexFile& dex_file,
- const DexFile::CodeItem* code_item) {
- if (code_item != nullptr) {
- DCHECK(dex_file.IsInDataSection(code_item));
- if (dex_file.IsCompactDexFile()) {
- Init(down_cast<const CompactDexFile::CodeItem&>(*code_item));
- } else {
- DCHECK(dex_file.IsStandardDexFile());
- Init(down_cast<const StandardDexFile::CodeItem&>(*code_item));
- }
- }
-}
-
-inline CodeItemInstructionAccessor::CodeItemInstructionAccessor(
- const DexFile& dex_file,
- const DexFile::CodeItem* code_item) {
- Init(dex_file, code_item);
-}
-
-inline DexInstructionIterator CodeItemInstructionAccessor::begin() const {
- return DexInstructionIterator(insns_, 0u);
-}
-
-inline DexInstructionIterator CodeItemInstructionAccessor::end() const {
- return DexInstructionIterator(insns_, insns_size_in_code_units_);
-}
-
-inline IterationRange<DexInstructionIterator> CodeItemInstructionAccessor::InstructionsFrom(
- uint32_t start_dex_pc) const {
- DCHECK_LT(start_dex_pc, InsnsSizeInCodeUnits());
- return {
- DexInstructionIterator(insns_, start_dex_pc),
- DexInstructionIterator(insns_, insns_size_in_code_units_) };
-}
-
-inline void CodeItemDataAccessor::Init(const CompactDexFile::CodeItem& code_item) {
- uint32_t insns_size_in_code_units;
- code_item.DecodeFields</*kDecodeOnlyInstructionCount*/ false>(&insns_size_in_code_units,
- &registers_size_,
- &ins_size_,
- &outs_size_,
- &tries_size_);
- CodeItemInstructionAccessor::Init(insns_size_in_code_units, code_item.insns_);
-}
-
-inline void CodeItemDataAccessor::Init(const StandardDexFile::CodeItem& code_item) {
- CodeItemInstructionAccessor::Init(code_item);
- registers_size_ = code_item.registers_size_;
- ins_size_ = code_item.ins_size_;
- outs_size_ = code_item.outs_size_;
- tries_size_ = code_item.tries_size_;
-}
-
-inline void CodeItemDataAccessor::Init(const DexFile& dex_file,
- const DexFile::CodeItem* code_item) {
- if (code_item != nullptr) {
- if (dex_file.IsCompactDexFile()) {
- CodeItemDataAccessor::Init(down_cast<const CompactDexFile::CodeItem&>(*code_item));
- } else {
- DCHECK(dex_file.IsStandardDexFile());
- CodeItemDataAccessor::Init(down_cast<const StandardDexFile::CodeItem&>(*code_item));
- }
- }
-}
-
-inline CodeItemDataAccessor::CodeItemDataAccessor(const DexFile& dex_file,
- const DexFile::CodeItem* code_item) {
- Init(dex_file, code_item);
-}
-
-inline IterationRange<const DexFile::TryItem*> CodeItemDataAccessor::TryItems() const {
- const DexFile::TryItem* try_items = DexFile::GetTryItems(end(), 0u);
- return {
- try_items,
- try_items + TriesSize() };
-}
-
-inline const uint8_t* CodeItemDataAccessor::GetCatchHandlerData(size_t offset) const {
- return DexFile::GetCatchHandlerData(end(), TriesSize(), offset);
-}
-
-inline const DexFile::TryItem* CodeItemDataAccessor::FindTryItem(uint32_t try_dex_pc) const {
- IterationRange<const DexFile::TryItem*> try_items(TryItems());
- int32_t index = DexFile::FindTryItem(try_items.begin(),
- try_items.end() - try_items.begin(),
- try_dex_pc);
- return index != -1 ? &try_items.begin()[index] : nullptr;
-}
-
-inline const void* CodeItemDataAccessor::CodeItemDataEnd() const {
- const uint8_t* handler_data = GetCatchHandlerData();
-
- if (TriesSize() == 0 || handler_data == nullptr) {
- return &end().Inst();
- }
- // Get the start of the handler data.
- const uint32_t handlers_size = DecodeUnsignedLeb128(&handler_data);
- // Manually read each handler.
- for (uint32_t i = 0; i < handlers_size; ++i) {
- int32_t uleb128_count = DecodeSignedLeb128(&handler_data) * 2;
- if (uleb128_count <= 0) {
- uleb128_count = -uleb128_count + 1;
- }
- for (int32_t j = 0; j < uleb128_count; ++j) {
- DecodeUnsignedLeb128(&handler_data);
- }
- }
- return reinterpret_cast<const void*>(handler_data);
-}
-
-inline void CodeItemDebugInfoAccessor::Init(const DexFile& dex_file,
- const DexFile::CodeItem* code_item,
- uint32_t dex_method_index) {
- if (code_item == nullptr) {
- return;
- }
- dex_file_ = &dex_file;
- if (dex_file.IsCompactDexFile()) {
- Init(down_cast<const CompactDexFile::CodeItem&>(*code_item), dex_method_index);
- } else {
- DCHECK(dex_file.IsStandardDexFile());
- Init(down_cast<const StandardDexFile::CodeItem&>(*code_item));
- }
-}
-
-inline void CodeItemDebugInfoAccessor::Init(const CompactDexFile::CodeItem& code_item,
- uint32_t dex_method_index) {
- debug_info_offset_ = down_cast<const CompactDexFile*>(dex_file_)->GetDebugInfoOffset(
- dex_method_index);
- CodeItemDataAccessor::Init(code_item);
-}
-
-inline void CodeItemDebugInfoAccessor::Init(const StandardDexFile::CodeItem& code_item) {
- debug_info_offset_ = code_item.debug_info_off_;
- CodeItemDataAccessor::Init(code_item);
-}
-
-template<typename NewLocalCallback>
-inline bool CodeItemDebugInfoAccessor::DecodeDebugLocalInfo(bool is_static,
- uint32_t method_idx,
- NewLocalCallback new_local,
- void* context) const {
- return dex_file_->DecodeDebugLocalInfo(RegistersSize(),
- InsSize(),
- InsnsSizeInCodeUnits(),
- DebugInfoOffset(),
- is_static,
- method_idx,
- new_local,
- context);
-}
-
-} // namespace art
+// TODO: delete this file once system/core is updated.
+#include "code_item_accessors-inl.h"
#endif // ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_NO_ART_INL_H_
diff --git a/runtime/dex/code_item_accessors.h b/runtime/dex/code_item_accessors.h
index 08f823cae8..beb78f6e4f 100644
--- a/runtime/dex/code_item_accessors.h
+++ b/runtime/dex/code_item_accessors.h
@@ -85,8 +85,6 @@ class CodeItemDataAccessor : public CodeItemInstructionAccessor {
public:
ALWAYS_INLINE CodeItemDataAccessor(const DexFile& dex_file, const DexFile::CodeItem* code_item);
- ALWAYS_INLINE explicit CodeItemDataAccessor(ArtMethod* method);
-
uint16_t RegistersSize() const {
return registers_size_;
}
diff --git a/runtime/dex/code_item_accessors_test.cc b/runtime/dex/code_item_accessors_test.cc
index 8e2548bf3d..1bd12a6f09 100644
--- a/runtime/dex/code_item_accessors_test.cc
+++ b/runtime/dex/code_item_accessors_test.cc
@@ -16,6 +16,7 @@
#include "code_item_accessors-inl.h"
+#include <sys/mman.h>
#include <memory>
#include "common_runtime_test.h"
diff --git a/runtime/dex/compact_dex_file.cc b/runtime/dex/compact_dex_file.cc
index 37f5d0074c..ce289d4d7b 100644
--- a/runtime/dex/compact_dex_file.cc
+++ b/runtime/dex/compact_dex_file.cc
@@ -16,7 +16,7 @@
#include "compact_dex_file.h"
-#include "code_item_accessors-no_art-inl.h"
+#include "code_item_accessors-inl.h"
#include "dex_file-inl.h"
#include "leb128.h"
diff --git a/runtime/dex/descriptors_names.cc b/runtime/dex/descriptors_names.cc
new file mode 100644
index 0000000000..8124e7256f
--- /dev/null
+++ b/runtime/dex/descriptors_names.cc
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "descriptors_names.h"
+
+#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
+
+#include "dex/utf-inl.h"
+
+namespace art {
+
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+
+void AppendPrettyDescriptor(const char* descriptor, std::string* result) {
+ // Count the number of '['s to get the dimensionality.
+ const char* c = descriptor;
+ size_t dim = 0;
+ while (*c == '[') {
+ dim++;
+ c++;
+ }
+
+ // Reference or primitive?
+ if (*c == 'L') {
+ // "[[La/b/C;" -> "a.b.C[][]".
+ c++; // Skip the 'L'.
+ } else {
+ // "[[B" -> "byte[][]".
+ // To make life easier, we make primitives look like unqualified
+ // reference types.
+ switch (*c) {
+ case 'B': c = "byte;"; break;
+ case 'C': c = "char;"; break;
+ case 'D': c = "double;"; break;
+ case 'F': c = "float;"; break;
+ case 'I': c = "int;"; break;
+ case 'J': c = "long;"; break;
+ case 'S': c = "short;"; break;
+ case 'Z': c = "boolean;"; break;
+ case 'V': c = "void;"; break; // Used when decoding return types.
+ default: result->append(descriptor); return;
+ }
+ }
+
+ // At this point, 'c' is a string of the form "fully/qualified/Type;"
+ // or "primitive;". Rewrite the type with '.' instead of '/':
+ const char* p = c;
+ while (*p != ';') {
+ char ch = *p++;
+ if (ch == '/') {
+ ch = '.';
+ }
+ result->push_back(ch);
+ }
+ // ...and replace the semicolon with 'dim' "[]" pairs:
+ for (size_t i = 0; i < dim; ++i) {
+ result->append("[]");
+ }
+}
+
+std::string PrettyDescriptor(const char* descriptor) {
+ std::string result;
+ AppendPrettyDescriptor(descriptor, &result);
+ return result;
+}
+
+std::string GetJniShortName(const std::string& class_descriptor, const std::string& method) {
+ // Remove the leading 'L' and trailing ';'...
+ std::string class_name(class_descriptor);
+ CHECK_EQ(class_name[0], 'L') << class_name;
+ CHECK_EQ(class_name[class_name.size() - 1], ';') << class_name;
+ class_name.erase(0, 1);
+ class_name.erase(class_name.size() - 1, 1);
+
+ std::string short_name;
+ short_name += "Java_";
+ short_name += MangleForJni(class_name);
+ short_name += "_";
+ short_name += MangleForJni(method);
+ return short_name;
+}
+
+// See http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/design.html#wp615 for the full rules.
+std::string MangleForJni(const std::string& s) {
+ std::string result;
+ size_t char_count = CountModifiedUtf8Chars(s.c_str());
+ const char* cp = &s[0];
+ for (size_t i = 0; i < char_count; ++i) {
+ uint32_t ch = GetUtf16FromUtf8(&cp);
+ if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) {
+ result.push_back(ch);
+ } else if (ch == '.' || ch == '/') {
+ result += "_";
+ } else if (ch == '_') {
+ result += "_1";
+ } else if (ch == ';') {
+ result += "_2";
+ } else if (ch == '[') {
+ result += "_3";
+ } else {
+ const uint16_t leading = GetLeadingUtf16Char(ch);
+ const uint32_t trailing = GetTrailingUtf16Char(ch);
+
+ StringAppendF(&result, "_0%04x", leading);
+ if (trailing != 0) {
+ StringAppendF(&result, "_0%04x", trailing);
+ }
+ }
+ }
+ return result;
+}
+
+std::string DotToDescriptor(const char* class_name) {
+ std::string descriptor(class_name);
+ std::replace(descriptor.begin(), descriptor.end(), '.', '/');
+ if (descriptor.length() > 0 && descriptor[0] != '[') {
+ descriptor = "L" + descriptor + ";";
+ }
+ return descriptor;
+}
+
+std::string DescriptorToDot(const char* descriptor) {
+ size_t length = strlen(descriptor);
+ if (length > 1) {
+ if (descriptor[0] == 'L' && descriptor[length - 1] == ';') {
+ // Descriptors have the leading 'L' and trailing ';' stripped.
+ std::string result(descriptor + 1, length - 2);
+ std::replace(result.begin(), result.end(), '/', '.');
+ return result;
+ } else {
+ // For arrays the 'L' and ';' remain intact.
+ std::string result(descriptor);
+ std::replace(result.begin(), result.end(), '/', '.');
+ return result;
+ }
+ }
+ // Do nothing for non-class/array descriptors.
+ return descriptor;
+}
+
+std::string DescriptorToName(const char* descriptor) {
+ size_t length = strlen(descriptor);
+ if (descriptor[0] == 'L' && descriptor[length - 1] == ';') {
+ std::string result(descriptor + 1, length - 2);
+ return result;
+ }
+ return descriptor;
+}
+
+// Helper for IsValidPartOfMemberNameUtf8(), a bit vector indicating valid low ascii.
+static uint32_t DEX_MEMBER_VALID_LOW_ASCII[4] = {
+ 0x00000000, // 00..1f low control characters; nothing valid
+ 0x03ff2010, // 20..3f digits and symbols; valid: '0'..'9', '$', '-'
+ 0x87fffffe, // 40..5f uppercase etc.; valid: 'A'..'Z', '_'
+ 0x07fffffe // 60..7f lowercase etc.; valid: 'a'..'z'
+};
+
+// Helper for IsValidPartOfMemberNameUtf8(); do not call directly.
+static bool IsValidPartOfMemberNameUtf8Slow(const char** pUtf8Ptr) {
+ /*
+ * It's a multibyte encoded character. Decode it and analyze. We
+ * accept anything that isn't (a) an improperly encoded low value,
+ * (b) an improper surrogate pair, (c) an encoded '\0', (d) a high
+ * control character, or (e) a high space, layout, or special
+ * character (U+00a0, U+2000..U+200f, U+2028..U+202f,
+ * U+fff0..U+ffff). This is all specified in the dex format
+ * document.
+ */
+
+ const uint32_t pair = GetUtf16FromUtf8(pUtf8Ptr);
+ const uint16_t leading = GetLeadingUtf16Char(pair);
+
+ // We have a surrogate pair resulting from a valid 4 byte UTF sequence.
+ // No further checks are necessary because 4 byte sequences span code
+ // points [U+10000, U+1FFFFF], which are valid codepoints in a dex
+ // identifier. Furthermore, GetUtf16FromUtf8 guarantees that each of
+ // the surrogate halves are valid and well formed in this instance.
+ if (GetTrailingUtf16Char(pair) != 0) {
+ return true;
+ }
+
+
+ // We've encountered a one, two or three byte UTF-8 sequence. The
+ // three byte UTF-8 sequence could be one half of a surrogate pair.
+ switch (leading >> 8) {
+ case 0x00:
+ // It's only valid if it's above the ISO-8859-1 high space (0xa0).
+ return (leading > 0x00a0);
+ case 0xd8:
+ case 0xd9:
+ case 0xda:
+ case 0xdb:
+ {
+ // We found a three byte sequence encoding one half of a surrogate.
+ // Look for the other half.
+ const uint32_t pair2 = GetUtf16FromUtf8(pUtf8Ptr);
+ const uint16_t trailing = GetLeadingUtf16Char(pair2);
+
+ return (GetTrailingUtf16Char(pair2) == 0) && (0xdc00 <= trailing && trailing <= 0xdfff);
+ }
+ case 0xdc:
+ case 0xdd:
+ case 0xde:
+ case 0xdf:
+ // It's a trailing surrogate, which is not valid at this point.
+ return false;
+ case 0x20:
+ case 0xff:
+ // It's in the range that has spaces, controls, and specials.
+ switch (leading & 0xfff8) {
+ case 0x2000:
+ case 0x2008:
+ case 0x2028:
+ case 0xfff0:
+ case 0xfff8:
+ return false;
+ }
+ return true;
+ default:
+ return true;
+ }
+
+ UNREACHABLE();
+}
+
+/* Return whether the pointed-at modified-UTF-8 encoded character is
+ * valid as part of a member name, updating the pointer to point past
+ * the consumed character. This will consume two encoded UTF-16 code
+ * points if the character is encoded as a surrogate pair. Also, if
+ * this function returns false, then the given pointer may only have
+ * been partially advanced.
+ */
+static bool IsValidPartOfMemberNameUtf8(const char** pUtf8Ptr) {
+ uint8_t c = (uint8_t) **pUtf8Ptr;
+ if (LIKELY(c <= 0x7f)) {
+ // It's low-ascii, so check the table.
+ uint32_t wordIdx = c >> 5;
+ uint32_t bitIdx = c & 0x1f;
+ (*pUtf8Ptr)++;
+ return (DEX_MEMBER_VALID_LOW_ASCII[wordIdx] & (1 << bitIdx)) != 0;
+ }
+
+ // It's a multibyte encoded character. Call a non-inline function
+ // for the heavy lifting.
+ return IsValidPartOfMemberNameUtf8Slow(pUtf8Ptr);
+}
+
+bool IsValidMemberName(const char* s) {
+ bool angle_name = false;
+
+ switch (*s) {
+ case '\0':
+ // The empty string is not a valid name.
+ return false;
+ case '<':
+ angle_name = true;
+ s++;
+ break;
+ }
+
+ while (true) {
+ switch (*s) {
+ case '\0':
+ return !angle_name;
+ case '>':
+ return angle_name && s[1] == '\0';
+ }
+
+ if (!IsValidPartOfMemberNameUtf8(&s)) {
+ return false;
+ }
+ }
+}
+
+enum ClassNameType { kName, kDescriptor };
+template<ClassNameType kType, char kSeparator>
+static bool IsValidClassName(const char* s) {
+ int arrayCount = 0;
+ while (*s == '[') {
+ arrayCount++;
+ s++;
+ }
+
+ if (arrayCount > 255) {
+ // Arrays may have no more than 255 dimensions.
+ return false;
+ }
+
+ ClassNameType type = kType;
+ if (type != kDescriptor && arrayCount != 0) {
+ /*
+ * If we're looking at an array of some sort, then it doesn't
+ * matter if what is being asked for is a class name; the
+ * format looks the same as a type descriptor in that case, so
+ * treat it as such.
+ */
+ type = kDescriptor;
+ }
+
+ if (type == kDescriptor) {
+ /*
+ * We are looking for a descriptor. Either validate it as a
+ * single-character primitive type, or continue on to check the
+ * embedded class name (bracketed by "L" and ";").
+ */
+ switch (*(s++)) {
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'I':
+ case 'J':
+ case 'S':
+ case 'Z':
+ // These are all single-character descriptors for primitive types.
+ return (*s == '\0');
+ case 'V':
+ // Non-array void is valid, but you can't have an array of void.
+ return (arrayCount == 0) && (*s == '\0');
+ case 'L':
+ // Class name: Break out and continue below.
+ break;
+ default:
+ // Oddball descriptor character.
+ return false;
+ }
+ }
+
+ /*
+ * We just consumed the 'L' that introduces a class name as part
+ * of a type descriptor, or we are looking for an unadorned class
+ * name.
+ */
+
+ bool sepOrFirst = true; // first character or just encountered a separator.
+ for (;;) {
+ uint8_t c = (uint8_t) *s;
+ switch (c) {
+ case '\0':
+ /*
+ * Premature end for a type descriptor, but valid for
+ * a class name as long as we haven't encountered an
+ * empty component (including the degenerate case of
+ * the empty string "").
+ */
+ return (type == kName) && !sepOrFirst;
+ case ';':
+ /*
+ * Invalid character for a class name, but the
+ * legitimate end of a type descriptor. In the latter
+ * case, make sure that this is the end of the string
+ * and that it doesn't end with an empty component
+ * (including the degenerate case of "L;").
+ */
+ return (type == kDescriptor) && !sepOrFirst && (s[1] == '\0');
+ case '/':
+ case '.':
+ if (c != kSeparator) {
+ // The wrong separator character.
+ return false;
+ }
+ if (sepOrFirst) {
+ // Separator at start or two separators in a row.
+ return false;
+ }
+ sepOrFirst = true;
+ s++;
+ break;
+ default:
+ if (!IsValidPartOfMemberNameUtf8(&s)) {
+ return false;
+ }
+ sepOrFirst = false;
+ break;
+ }
+ }
+}
+
+bool IsValidBinaryClassName(const char* s) {
+ return IsValidClassName<kName, '.'>(s);
+}
+
+bool IsValidJniClassName(const char* s) {
+ return IsValidClassName<kName, '/'>(s);
+}
+
+bool IsValidDescriptor(const char* s) {
+ return IsValidClassName<kDescriptor, '/'>(s);
+}
+
+void Split(const std::string& s, char separator, std::vector<std::string>* result) {
+ const char* p = s.data();
+ const char* end = p + s.size();
+ while (p != end) {
+ if (*p == separator) {
+ ++p;
+ } else {
+ const char* start = p;
+ while (++p != end && *p != separator) {
+ // Skip to the next occurrence of the separator.
+ }
+ result->push_back(std::string(start, p - start));
+ }
+ }
+}
+
+std::string PrettyDescriptor(Primitive::Type type) {
+ return PrettyDescriptor(Primitive::Descriptor(type));
+}
+
+} // namespace art
diff --git a/runtime/dex/descriptors_names.h b/runtime/dex/descriptors_names.h
new file mode 100644
index 0000000000..22e9573556
--- /dev/null
+++ b/runtime/dex/descriptors_names.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_DEX_DESCRIPTORS_NAMES_H_
+#define ART_RUNTIME_DEX_DESCRIPTORS_NAMES_H_
+
+#include <string>
+
+#include "primitive.h"
+
+namespace art {
+
+// Used to implement PrettyClass, PrettyField, PrettyMethod, and PrettyTypeOf,
+// one of which is probably more useful to you.
+// Returns a human-readable equivalent of 'descriptor'. So "I" would be "int",
+// "[[I" would be "int[][]", "[Ljava/lang/String;" would be
+// "java.lang.String[]", and so forth.
+void AppendPrettyDescriptor(const char* descriptor, std::string* result);
+std::string PrettyDescriptor(const char* descriptor);
+std::string PrettyDescriptor(Primitive::Type type);
+
+// Performs JNI name mangling as described in section 11.3 "Linking Native Methods"
+// of the JNI spec.
+std::string MangleForJni(const std::string& s);
+
+std::string GetJniShortName(const std::string& class_name, const std::string& method_name);
+
+// Turn "java.lang.String" into "Ljava/lang/String;".
+std::string DotToDescriptor(const char* class_name);
+
+// Turn "Ljava/lang/String;" into "java.lang.String" using the conventions of
+// java.lang.Class.getName().
+std::string DescriptorToDot(const char* descriptor);
+
+// Turn "Ljava/lang/String;" into "java/lang/String" using the opposite conventions of
+// java.lang.Class.getName().
+std::string DescriptorToName(const char* descriptor);
+
+// Tests for whether 's' is a valid class name in the three common forms:
+bool IsValidBinaryClassName(const char* s); // "java.lang.String"
+bool IsValidJniClassName(const char* s); // "java/lang/String"
+bool IsValidDescriptor(const char* s); // "Ljava/lang/String;"
+
+// Returns whether the given string is a valid field or method name,
+// additionally allowing names that begin with '<' and end with '>'.
+bool IsValidMemberName(const char* s);
+
+} // namespace art
+
+#endif // ART_RUNTIME_DEX_DESCRIPTORS_NAMES_H_
diff --git a/runtime/dex/dex_file.cc b/runtime/dex/dex_file.cc
index 6ec997c93f..18eb903551 100644
--- a/runtime/dex/dex_file.cc
+++ b/runtime/dex/dex_file.cc
@@ -30,11 +30,11 @@
#include "base/enums.h"
#include "base/stl_util.h"
+#include "descriptors_names.h"
#include "dex_file-inl.h"
#include "leb128.h"
#include "standard_dex_file.h"
#include "utf-inl.h"
-#include "utils.h"
namespace art {
@@ -157,14 +157,14 @@ bool DexFile::CheckMagicAndVersion(std::string* error_msg) const {
void DexFile::InitializeSectionsFromMapList() {
const MapList* map_list = reinterpret_cast<const MapList*>(DataBegin() + header_->map_off_);
- if (header_->map_off_ == 0 || header_->map_off_ > size_) {
+ if (header_->map_off_ == 0 || header_->map_off_ > DataSize()) {
// Bad offset. The dex file verifier runs after this method and will reject the file.
return;
}
const size_t count = map_list->size_;
size_t map_limit = header_->map_off_ + count * sizeof(MapItem);
- if (header_->map_off_ >= map_limit || map_limit > size_) {
+ if (header_->map_off_ >= map_limit || map_limit > DataSize()) {
// Overflow or out out of bounds. The dex file verifier runs after
// this method and will reject the file as it is malformed.
return;
@@ -173,10 +173,10 @@ void DexFile::InitializeSectionsFromMapList() {
for (size_t i = 0; i < count; ++i) {
const MapItem& map_item = map_list->list_[i];
if (map_item.type_ == kDexTypeMethodHandleItem) {
- method_handles_ = reinterpret_cast<const MethodHandleItem*>(DataBegin() + map_item.offset_);
+ method_handles_ = reinterpret_cast<const MethodHandleItem*>(Begin() + map_item.offset_);
num_method_handles_ = map_item.size_;
} else if (map_item.type_ == kDexTypeCallSiteIdItem) {
- call_site_ids_ = reinterpret_cast<const CallSiteIdItem*>(DataBegin() + map_item.offset_);
+ call_site_ids_ = reinterpret_cast<const CallSiteIdItem*>(Begin() + map_item.offset_);
num_call_site_ids_ = map_item.size_;
}
}
diff --git a/runtime/dex/dex_file.h b/runtime/dex/dex_file.h
index ef25797274..7e2fe98923 100644
--- a/runtime/dex/dex_file.h
+++ b/runtime/dex/dex_file.h
@@ -138,9 +138,6 @@ class DexFile {
uint16_t unused_;
uint32_t size_;
uint32_t offset_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MapItem);
};
struct MapList {
diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc
index 72b18fb420..e01890f541 100644
--- a/runtime/dex/dex_file_annotations.cc
+++ b/runtime/dex/dex_file_annotations.cc
@@ -1559,7 +1559,7 @@ int32_t GetLineNumFromPC(const DexFile* dex_file, ArtMethod* method, uint32_t re
return -2;
}
- CodeItemDebugInfoAccessor accessor(method);
+ CodeItemDebugInfoAccessor accessor(method->DexInstructionDebugInfo());
DCHECK(accessor.HasCodeItem()) << method->PrettyMethod() << " " << dex_file->GetLocation();
// A method with no line number info should return -1
diff --git a/runtime/dex/dex_file_exception_helpers.cc b/runtime/dex/dex_file_exception_helpers.cc
index ad56eb0a0b..8e597fd3dd 100644
--- a/runtime/dex/dex_file_exception_helpers.cc
+++ b/runtime/dex/dex_file_exception_helpers.cc
@@ -16,7 +16,7 @@
#include "dex_file_exception_helpers.h"
-#include "code_item_accessors-no_art-inl.h"
+#include "code_item_accessors-inl.h"
namespace art {
diff --git a/runtime/dex/dex_file_layout.cc b/runtime/dex/dex_file_layout.cc
index 1973440d55..312898d82f 100644
--- a/runtime/dex/dex_file_layout.cc
+++ b/runtime/dex/dex_file_layout.cc
@@ -19,8 +19,8 @@
#include <sys/mman.h>
#include "base/file_utils.h"
+#include "descriptors_names.h"
#include "dex_file.h"
-#include "utils.h"
namespace art {
diff --git a/runtime/dex/dex_file_loader.cc b/runtime/dex/dex_file_loader.cc
index 7dde0a42fd..0f2758e372 100644
--- a/runtime/dex/dex_file_loader.cc
+++ b/runtime/dex/dex_file_loader.cc
@@ -222,8 +222,8 @@ std::unique_ptr<const DexFile> DexFileLoader::Open(const uint8_t* base,
std::string* error_msg) const {
return OpenCommon(base,
size,
- /*data_base*/ base,
- /*data_size*/ size,
+ /*data_base*/ nullptr,
+ /*data_size*/ 0,
location,
location_checksum,
oat_dex_file,
diff --git a/runtime/dex/dex_file_test.cc b/runtime/dex/dex_file_test.cc
index cb721af754..998bfd6c7f 100644
--- a/runtime/dex/dex_file_test.cc
+++ b/runtime/dex/dex_file_test.cc
@@ -25,13 +25,13 @@
#include "base/unix_file/fd_file.h"
#include "code_item_accessors-inl.h"
#include "common_runtime_test.h"
+#include "descriptors_names.h"
#include "dex_file-inl.h"
#include "dex_file_loader.h"
#include "mem_map.h"
#include "os.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
-#include "utils.h"
namespace art {
diff --git a/runtime/dex/dex_file_verifier.cc b/runtime/dex/dex_file_verifier.cc
index f7fdbb027c..62667052ad 100644
--- a/runtime/dex/dex_file_verifier.cc
+++ b/runtime/dex/dex_file_verifier.cc
@@ -23,14 +23,12 @@
#include "android-base/stringprintf.h"
-#include "code_item_accessors-no_art-inl.h"
+#include "code_item_accessors-inl.h"
+#include "descriptors_names.h"
#include "dex_file-inl.h"
-#include "experimental_flags.h"
#include "leb128.h"
#include "modifiers.h"
-#include "safe_map.h"
#include "utf-inl.h"
-#include "utils.h"
namespace art {
diff --git a/runtime/dex/dex_file_verifier_test.cc b/runtime/dex/dex_file_verifier_test.cc
index 9759685961..d73a7fbfa3 100644
--- a/runtime/dex/dex_file_verifier_test.cc
+++ b/runtime/dex/dex_file_verifier_test.cc
@@ -27,6 +27,7 @@
#include "base/macros.h"
#include "base/unix_file/fd_file.h"
#include "common_runtime_test.h"
+#include "descriptors_names.h"
#include "dex_file-inl.h"
#include "dex_file_loader.h"
#include "dex_file_types.h"
@@ -34,7 +35,6 @@
#include "scoped_thread_state_change-inl.h"
#include "standard_dex_file.h"
#include "thread-current-inl.h"
-#include "utils.h"
namespace art {
diff --git a/runtime/dex/dex_instruction.cc b/runtime/dex/dex_instruction.cc
index 6ebe2286e8..b84791ffae 100644
--- a/runtime/dex/dex_instruction.cc
+++ b/runtime/dex/dex_instruction.cc
@@ -24,7 +24,7 @@
#include "android-base/stringprintf.h"
#include "dex_file-inl.h"
-#include "utils.h"
+#include "utf.h"
namespace art {
diff --git a/runtime/dex/standard_dex_file.cc b/runtime/dex/standard_dex_file.cc
index 024f73b4e5..f7317eb997 100644
--- a/runtime/dex/standard_dex_file.cc
+++ b/runtime/dex/standard_dex_file.cc
@@ -17,7 +17,7 @@
#include "standard_dex_file.h"
#include "base/casts.h"
-#include "code_item_accessors-no_art-inl.h"
+#include "code_item_accessors-inl.h"
#include "dex_file-inl.h"
#include "leb128.h"
diff --git a/runtime/utf-inl.h b/runtime/dex/utf-inl.h
index b2d6765fb0..4f626a8580 100644
--- a/runtime/utf-inl.h
+++ b/runtime/dex/utf-inl.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_UTF_INL_H_
-#define ART_RUNTIME_UTF_INL_H_
+#ifndef ART_RUNTIME_DEX_UTF_INL_H_
+#define ART_RUNTIME_DEX_UTF_INL_H_
#include "utf.h"
@@ -96,4 +96,4 @@ inline int CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(const char* u
} // namespace art
-#endif // ART_RUNTIME_UTF_INL_H_
+#endif // ART_RUNTIME_DEX_UTF_INL_H_
diff --git a/runtime/utf.cc b/runtime/dex/utf.cc
index 32ae187297..772a610140 100644
--- a/runtime/utf.cc
+++ b/runtime/dex/utf.cc
@@ -17,12 +17,17 @@
#include "utf.h"
#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include "base/casts.h"
#include "utf-inl.h"
namespace art {
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+
// This is used only from debugger and test code.
size_t CountModifiedUtf8Chars(const char* utf8) {
return CountModifiedUtf8Chars(utf8, strlen(utf8));
@@ -262,4 +267,55 @@ size_t CountUtf8Bytes(const uint16_t* chars, size_t char_count) {
return result;
}
+static inline constexpr bool NeedsEscaping(uint16_t ch) {
+ return (ch < ' ' || ch > '~');
+}
+
+std::string PrintableChar(uint16_t ch) {
+ std::string result;
+ result += '\'';
+ if (NeedsEscaping(ch)) {
+ StringAppendF(&result, "\\u%04x", ch);
+ } else {
+ result += static_cast<std::string::value_type>(ch);
+ }
+ result += '\'';
+ return result;
+}
+
+std::string PrintableString(const char* utf) {
+ std::string result;
+ result += '"';
+ const char* p = utf;
+ size_t char_count = CountModifiedUtf8Chars(p);
+ for (size_t i = 0; i < char_count; ++i) {
+ uint32_t ch = GetUtf16FromUtf8(&p);
+ if (ch == '\\') {
+ result += "\\\\";
+ } else if (ch == '\n') {
+ result += "\\n";
+ } else if (ch == '\r') {
+ result += "\\r";
+ } else if (ch == '\t') {
+ result += "\\t";
+ } else {
+ const uint16_t leading = GetLeadingUtf16Char(ch);
+
+ if (NeedsEscaping(leading)) {
+ StringAppendF(&result, "\\u%04x", leading);
+ } else {
+ result += static_cast<std::string::value_type>(leading);
+ }
+
+ const uint32_t trailing = GetTrailingUtf16Char(ch);
+ if (trailing != 0) {
+ // All high surrogates will need escaping.
+ StringAppendF(&result, "\\u%04x", trailing);
+ }
+ }
+ }
+ result += '"';
+ return result;
+}
+
} // namespace art
diff --git a/runtime/utf.h b/runtime/dex/utf.h
index cbb32fa6cd..4adfc4af8c 100644
--- a/runtime/utf.h
+++ b/runtime/dex/utf.h
@@ -14,14 +14,16 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_UTF_H_
-#define ART_RUNTIME_UTF_H_
+#ifndef ART_RUNTIME_DEX_UTF_H_
+#define ART_RUNTIME_DEX_UTF_H_
#include "base/macros.h"
#include <stddef.h>
#include <stdint.h>
+#include <string>
+
/*
* All UTF-8 in art is actually modified UTF-8. Mostly, this distinction
* doesn't matter.
@@ -121,6 +123,13 @@ ALWAYS_INLINE uint16_t GetLeadingUtf16Char(uint32_t maybe_pair);
*/
ALWAYS_INLINE uint16_t GetTrailingUtf16Char(uint32_t maybe_pair);
+// Returns a printable (escaped) version of a character.
+std::string PrintableChar(uint16_t ch);
+
+// Returns an ASCII string corresponding to the given UTF-8 string.
+// Java escapes are used for non-ASCII characters.
+std::string PrintableString(const char* utf8);
+
} // namespace art
-#endif // ART_RUNTIME_UTF_H_
+#endif // ART_RUNTIME_DEX_UTF_H_
diff --git a/runtime/utf_test.cc b/runtime/dex/utf_test.cc
index d1e97515d3..d1e97515d3 100644
--- a/runtime/utf_test.cc
+++ b/runtime/dex/utf_test.cc
diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
index 565b4edcc3..9b0756b529 100644
--- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "art_method-inl.h"
#include "callee_save_frame.h"
#include "common_throws.h"
#include "mirror/object-inl.h"
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index f727690c11..c5157ce9f4 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -785,7 +785,7 @@ extern "C" uint64_t artQuickToInterpreterBridge(ArtMethod* method, Thread* self,
uint32_t shorty_len = 0;
ArtMethod* non_proxy_method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize);
DCHECK(non_proxy_method->GetCodeItem() != nullptr) << method->PrettyMethod();
- CodeItemDataAccessor accessor(non_proxy_method);
+ CodeItemDataAccessor accessor(non_proxy_method->DexInstructionData());
const char* shorty = non_proxy_method->GetShorty(&shorty_len);
JValue result;
@@ -1121,7 +1121,7 @@ extern "C" const void* artQuickResolutionTrampoline(
// code.
if (!found_stack_map || kIsDebugBuild) {
uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
- CodeItemInstructionAccessor accessor(caller);
+ CodeItemInstructionAccessor accessor(caller->DexInstructions());
CHECK_LT(dex_pc, accessor.InsnsSizeInCodeUnits());
const Instruction& instr = accessor.InstructionAt(dex_pc);
Instruction::Code instr_code = instr.Opcode();
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 52dd104ac8..6735961591 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -106,8 +106,8 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self,
pre_fence_visitor(obj, usable_size);
QuasiAtomic::ThreadFenceForConstructor();
} else {
- // bytes allocated that takes bulk thread-local buffer allocations into account.
- size_t bytes_tl_bulk_allocated = 0;
+ // Bytes allocated that takes bulk thread-local buffer allocations into account.
+ size_t bytes_tl_bulk_allocated = 0u;
obj = TryToAllocate<kInstrumented, false>(self, allocator, byte_count, &bytes_allocated,
&usable_size, &bytes_tl_bulk_allocated);
if (UNLIKELY(obj == nullptr)) {
@@ -154,12 +154,13 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self,
}
pre_fence_visitor(obj, usable_size);
QuasiAtomic::ThreadFenceForConstructor();
- new_num_bytes_allocated = num_bytes_allocated_.FetchAndAddRelaxed(bytes_tl_bulk_allocated) +
- bytes_tl_bulk_allocated;
+ size_t num_bytes_allocated_before =
+ num_bytes_allocated_.FetchAndAddRelaxed(bytes_tl_bulk_allocated);
+ new_num_bytes_allocated = num_bytes_allocated_before + bytes_tl_bulk_allocated;
if (bytes_tl_bulk_allocated > 0) {
// Only trace when we get an increase in the number of bytes allocated. This happens when
// obtaining a new TLAB and isn't often enough to hurt performance according to golem.
- TraceHeapSize(new_num_bytes_allocated + bytes_tl_bulk_allocated);
+ TraceHeapSize(new_num_bytes_allocated);
}
}
if (kIsDebugBuild && Runtime::Current()->IsStarted()) {
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index de3a51a2ac..f476028d5f 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -24,144 +24,110 @@
namespace art {
namespace hiddenapi {
-// Returns true if member with `access flags` should only be accessed from
-// boot class path.
-inline bool IsMemberHidden(uint32_t access_flags) {
- if (!Runtime::Current()->AreHiddenApiChecksEnabled()) {
- return false;
- }
+enum Action {
+ kAllow,
+ kAllowButWarn,
+ kDeny
+};
+inline Action GetMemberAction(uint32_t access_flags) {
switch (HiddenApiAccessFlags::DecodeFromRuntime(access_flags)) {
case HiddenApiAccessFlags::kWhitelist:
+ return kAllow;
case HiddenApiAccessFlags::kLightGreylist:
case HiddenApiAccessFlags::kDarkGreylist:
- return false;
+ return kAllowButWarn;
case HiddenApiAccessFlags::kBlacklist:
- return true;
+ return kDeny;
}
}
-// Returns true if we should warn about non-boot class path accessing member
-// with `access_flags`.
-inline bool ShouldWarnAboutMember(uint32_t access_flags) {
- if (!Runtime::Current()->AreHiddenApiChecksEnabled()) {
- return false;
- }
-
- switch (HiddenApiAccessFlags::DecodeFromRuntime(access_flags)) {
- case HiddenApiAccessFlags::kWhitelist:
- return false;
- case HiddenApiAccessFlags::kLightGreylist:
- case HiddenApiAccessFlags::kDarkGreylist:
- return true;
- case HiddenApiAccessFlags::kBlacklist:
- // We should never access a blacklisted member from non-boot class path,
- // but this function is called before we establish the origin of the access.
- // Return false here, we do not want to warn when boot class path accesses
- // a blacklisted member.
- return false;
- }
-}
-
-// Returns true if caller `num_frames` up the stack is in boot class path.
-inline bool IsCallerInBootClassPath(Thread* self, size_t num_frames)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- ObjPtr<mirror::Class> klass = GetCallingClass(self, num_frames);
- if (klass == nullptr) {
- // Unattached native thread. Assume that this is *not* boot class path.
- return false;
- }
- return klass->IsBootStrapClassLoaded();
+// Issue a warning about field access.
+inline void WarnAboutMemberAccess(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::string tmp;
+ LOG(WARNING) << "Accessing hidden field "
+ << field->GetDeclaringClass()->GetDescriptor(&tmp) << "->"
+ << field->GetName() << ":" << field->GetTypeDescriptor();
}
-// Returns true if `caller` should not be allowed to access member with `access_flags`.
-inline bool ShouldBlockAccessToMember(uint32_t access_flags, mirror::Class* caller)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- return IsMemberHidden(access_flags) &&
- !caller->IsBootStrapClassLoaded();
+// Issue a warning about method access.
+inline void WarnAboutMemberAccess(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::string tmp;
+ LOG(WARNING) << "Accessing hidden method "
+ << method->GetDeclaringClass()->GetDescriptor(&tmp) << "->"
+ << method->GetName() << method->GetSignature().ToString();
}
-// Returns true if `caller` should not be allowed to access `member`.
+// Returns true if access to `member` should be denied to the caller of the
+// reflective query. The decision is based on whether the caller is in boot
+// class path or not. Because different users of this function determine this
+// in a different way, `fn_caller_in_boot(self)` is called and should return
+// true if the caller is in boot class path.
+// This function might print warnings into the log if the member is greylisted.
template<typename T>
-inline bool ShouldBlockAccessToMember(T* member, ArtMethod* caller)
+inline bool ShouldBlockAccessToMember(T* member,
+ Thread* self,
+ std::function<bool(Thread*)> fn_caller_in_boot)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(member != nullptr);
- DCHECK(!caller->IsRuntimeMethod());
- return ShouldBlockAccessToMember(member->GetAccessFlags(), caller->GetDeclaringClass());
-}
-// Returns true if the caller `num_frames` up the stack should not be allowed
-// to access `member`.
-template<typename T>
-inline bool ShouldBlockAccessToMember(T* member, Thread* self, size_t num_frames)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK(member != nullptr);
- return IsMemberHidden(member->GetAccessFlags()) &&
- !IsCallerInBootClassPath(self, num_frames); // This is expensive. Save it for last.
-}
+ if (!Runtime::Current()->AreHiddenApiChecksEnabled()) {
+ // Exit early. Nothing to enforce.
+ return false;
+ }
-// Issue a warning about field access.
-inline void WarnAboutMemberAccess(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) {
- Runtime::Current()->SetPendingHiddenApiWarning(true);
- LOG(WARNING) << "Access to hidden field " << field->PrettyField();
-}
+ Action action = GetMemberAction(member->GetAccessFlags());
+ if (action == kAllow) {
+ // Nothing to do.
+ return false;
+ }
-// Issue a warning about method access.
-inline void WarnAboutMemberAccess(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
- Runtime::Current()->SetPendingHiddenApiWarning(true);
- LOG(WARNING) << "Access to hidden method " << method->PrettyMethod();
-}
+ // Member is hidden. Walk the stack to find the caller.
+ // This can be *very* expensive. Save it for last.
+ if (fn_caller_in_boot(self)) {
+ // Caller in boot class path. Exit.
+ return false;
+ }
-// Set access flags of `member` to be in hidden API whitelist. This can be disabled
-// with a Runtime::SetDedupHiddenApiWarnings.
-template<typename T>
-inline void MaybeWhitelistMember(T* member) REQUIRES_SHARED(Locks::mutator_lock_) {
- if (Runtime::Current()->ShouldDedupeHiddenApiWarnings()) {
- member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
- member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
- DCHECK(!ShouldWarnAboutMember(member->GetAccessFlags()));
+ // Member is hidden and we are not in the boot class path. Act accordingly.
+ if (action == kAllowButWarn) {
+ // Allow access to this member but print a warning. Depending on a runtime
+ // flag, we might move the member into whitelist and skip the warning the
+ // next time the member is used.
+ Runtime::Current()->SetPendingHiddenApiWarning(true);
+ if (Runtime::Current()->ShouldDedupeHiddenApiWarnings()) {
+ member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
+ member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
+ }
+ WarnAboutMemberAccess(member);
+ return false;
+ } else {
+ DCHECK_EQ(action, hiddenapi::kDeny);
+ return true;
}
}
-// Check if `caller` should be allowed to access `member` and warn if not.
-template<typename T>
-inline void MaybeWarnAboutMemberAccess(T* member, ArtMethod* caller)
+// Returns true if access to member with `access_flags` should be denied to `caller`.
+// This function should be called on statically linked uses of hidden API.
+inline bool ShouldBlockAccessToMember(uint32_t access_flags, mirror::Class* caller)
REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK(member != nullptr);
- DCHECK(!caller->IsRuntimeMethod());
- if (!Runtime::Current()->AreHiddenApiChecksEnabled() ||
- member == nullptr ||
- !ShouldWarnAboutMember(member->GetAccessFlags()) ||
- caller->GetDeclaringClass()->IsBootStrapClassLoaded()) {
- return;
+ if (!Runtime::Current()->AreHiddenApiChecksEnabled()) {
+ // Exit early. Nothing to enforce.
+ return false;
}
- WarnAboutMember(member);
- MaybeWhitelistMember(member);
-}
-
-// Check if the caller `num_frames` up the stack should be allowed to access
-// `member` and warn if not.
-template<typename T>
-inline void MaybeWarnAboutMemberAccess(T* member, Thread* self, size_t num_frames)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (!Runtime::Current()->AreHiddenApiChecksEnabled() ||
- member == nullptr ||
- !ShouldWarnAboutMember(member->GetAccessFlags())) {
- return;
+ // Only continue if we want to deny access. Warnings are *not* printed.
+ if (GetMemberAction(access_flags) != kDeny) {
+ return false;
}
- // Walk the stack to find the caller. This is *very* expensive. Save it for last.
- ObjPtr<mirror::Class> klass = GetCallingClass(self, num_frames);
- if (klass == nullptr) {
- // Unattached native thread, assume that this is *not* boot class path
- // and enforce the rules.
- } else if (klass->IsBootStrapClassLoaded()) {
- return;
+ // Member is hidden. Check if the caller is in boot class path.
+ if (caller == nullptr) {
+ // The caller is unknown. We assume that this is *not* boot class path.
+ return true;
}
- WarnAboutMemberAccess(member);
- MaybeWhitelistMember(member);
+ return !caller->IsBootStrapClassLoaded();
}
} // namespace hiddenapi
diff --git a/runtime/imtable-inl.h b/runtime/imtable-inl.h
index 6237cca9e4..93346f6151 100644
--- a/runtime/imtable-inl.h
+++ b/runtime/imtable-inl.h
@@ -21,7 +21,7 @@
#include "art_method-inl.h"
#include "dex/dex_file.h"
-#include "utf.h"
+#include "dex/utf.h"
namespace art {
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 4524448916..24cedb093b 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -776,6 +776,17 @@ void Instrumentation::UpdateMethodsCodeImpl(ArtMethod* method, const void* quick
UpdateEntrypoints(method, new_quick_code);
}
+void Instrumentation::UpdateNativeMethodsCodeToJitCode(ArtMethod* method, const void* quick_code) {
+ // We don't do any read barrier on `method`'s declaring class in this code, as the JIT might
+ // enter here on a soon-to-be deleted ArtMethod. Updating the entrypoint is OK though, as
+ // the ArtMethod is still in memory.
+ const void* new_quick_code = quick_code;
+ if (UNLIKELY(instrumentation_stubs_installed_) && entry_exit_stubs_installed_) {
+ new_quick_code = GetQuickInstrumentationEntryPoint();
+ }
+ UpdateEntrypoints(method, new_quick_code);
+}
+
void Instrumentation::UpdateMethodsCode(ArtMethod* method, const void* quick_code) {
DCHECK(method->GetDeclaringClass()->IsResolved());
UpdateMethodsCodeImpl(method, quick_code);
@@ -1373,8 +1384,8 @@ TwoWordReturn Instrumentation::PopInstrumentationStackFrame(Thread* self,
reinterpret_cast<uintptr_t>(GetQuickDeoptimizationEntryPoint()));
} else {
if (deoptimize && !Runtime::Current()->IsAsyncDeoptimizeable(*return_pc)) {
- LOG(WARNING) << "Got a deoptimization request on un-deoptimizable " << method->PrettyMethod()
- << " at PC " << reinterpret_cast<void*>(*return_pc);
+ VLOG(deopt) << "Got a deoptimization request on un-deoptimizable " << method->PrettyMethod()
+ << " at PC " << reinterpret_cast<void*>(*return_pc);
}
if (kVerboseInstrumentation) {
LOG(INFO) << "Returning from " << method->PrettyMethod()
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index da63152d10..46b3f8d85f 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -280,6 +280,10 @@ class Instrumentation {
void UpdateMethodsCode(ArtMethod* method, const void* quick_code)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!deoptimized_methods_lock_);
+ // Update the code of a native method to a JITed stub.
+ void UpdateNativeMethodsCodeToJitCode(ArtMethod* method, const void* quick_code)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!deoptimized_methods_lock_);
+
// Update the code of a method to the interpreter respecting any installed stubs from debugger.
void UpdateMethodsCodeToInterpreterEntryPoint(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!deoptimized_methods_lock_);
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index 5b93d3b873..4b964f648b 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -18,6 +18,7 @@
#include <memory>
+#include "dex/utf.h"
#include "gc/collector/garbage_collector.h"
#include "gc/space/image_space.h"
#include "gc/weak_root_state.h"
@@ -30,7 +31,6 @@
#include "object_callbacks.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
-#include "utf.h"
namespace art {
diff --git a/runtime/intern_table_test.cc b/runtime/intern_table_test.cc
index 9c3ea8d864..b56c48d78c 100644
--- a/runtime/intern_table_test.cc
+++ b/runtime/intern_table_test.cc
@@ -18,12 +18,12 @@
#include "base/hash_set.h"
#include "common_runtime_test.h"
+#include "dex/utf.h"
#include "gc_root-inl.h"
#include "handle_scope-inl.h"
#include "mirror/object.h"
#include "mirror/string.h"
#include "scoped_thread_state_change-inl.h"
-#include "utf.h"
namespace art {
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 54db87297d..735c0e815a 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -389,7 +389,7 @@ void EnterInterpreterFromInvoke(Thread* self,
}
const char* old_cause = self->StartAssertNoThreadSuspension("EnterInterpreterFromInvoke");
- CodeItemDataAccessor accessor(method);
+ CodeItemDataAccessor accessor(method->DexInstructionData());
uint16_t num_regs;
uint16_t num_ins;
if (accessor.HasCodeItem()) {
@@ -499,7 +499,7 @@ void EnterInterpreterFromDeoptimize(Thread* self,
DCHECK(!shadow_frame->GetMethod()->MustCountLocks());
self->SetTopOfShadowStack(shadow_frame);
- CodeItemDataAccessor accessor(shadow_frame->GetMethod());
+ CodeItemDataAccessor accessor(shadow_frame->GetMethod()->DexInstructionData());
const uint32_t dex_pc = shadow_frame->GetDexPC();
uint32_t new_dex_pc = dex_pc;
if (UNLIKELY(self->IsExceptionPending())) {
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 475f93803d..d53da215a2 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -1320,7 +1320,7 @@ static inline bool DoCallCommon(ArtMethod* called_method,
}
// Compute method information.
- CodeItemDataAccessor accessor(called_method);
+ CodeItemDataAccessor accessor(called_method->DexInstructionData());
// Number of registers for the callee's call frame.
uint16_t num_regs;
// Test whether to use the interpreter or compiler entrypoint, and save that result to pass to
diff --git a/runtime/interpreter/shadow_frame.cc b/runtime/interpreter/shadow_frame.cc
index fe7e3e0a9b..264ec6a997 100644
--- a/runtime/interpreter/shadow_frame.cc
+++ b/runtime/interpreter/shadow_frame.cc
@@ -28,7 +28,7 @@ mirror::Object* ShadowFrame::GetThisObject() const {
return GetVRegReference(0);
} else {
CHECK(m->GetCodeItem() != nullptr) << ArtMethod::PrettyMethod(m);
- CodeItemDataAccessor accessor(m);
+ CodeItemDataAccessor accessor(m->DexInstructionData());
uint16_t reg = accessor.RegistersSize() - accessor.InsSize();
return GetVRegReference(reg);
}
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index b9b00519d1..85acc71377 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -176,6 +176,13 @@ static mirror::String* GetClassName(Thread* self, ShadowFrame* shadow_frame, siz
return param->AsString();
}
+template<typename T>
+static ALWAYS_INLINE bool ShouldBlockAccessToMember(T* member, ShadowFrame* frame)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return hiddenapi::ShouldBlockAccessToMember(
+ member->GetAccessFlags(), frame->GetMethod()->GetDeclaringClass());
+}
+
void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self,
ShadowFrame* shadow_frame,
JValue* result,
@@ -267,8 +274,7 @@ void UnstartedRuntime::UnstartedClassNewInstance(
auto* cl = Runtime::Current()->GetClassLinker();
if (cl->EnsureInitialized(self, h_klass, true, true)) {
ArtMethod* cons = h_klass->FindConstructor("()V", cl->GetImagePointerSize());
- if (cons != nullptr &&
- hiddenapi::ShouldBlockAccessToMember(cons, shadow_frame->GetMethod())) {
+ if (cons != nullptr && ShouldBlockAccessToMember(cons, shadow_frame)) {
cons = nullptr;
}
if (cons != nullptr) {
@@ -313,8 +319,7 @@ void UnstartedRuntime::UnstartedClassGetDeclaredField(
}
}
}
- if (found != nullptr &&
- hiddenapi::ShouldBlockAccessToMember(found, shadow_frame->GetMethod())) {
+ if (found != nullptr && ShouldBlockAccessToMember(found, shadow_frame)) {
found = nullptr;
}
if (found == nullptr) {
@@ -379,8 +384,7 @@ void UnstartedRuntime::UnstartedClassGetDeclaredMethod(
self, klass, name, args);
}
}
- if (method != nullptr &&
- hiddenapi::ShouldBlockAccessToMember(method->GetArtMethod(), shadow_frame->GetMethod())) {
+ if (method != nullptr && ShouldBlockAccessToMember(method->GetArtMethod(), shadow_frame)) {
method = nullptr;
}
result->SetL(method);
@@ -418,8 +422,7 @@ void UnstartedRuntime::UnstartedClassGetDeclaredConstructor(
}
}
if (constructor != nullptr &&
- hiddenapi::ShouldBlockAccessToMember(
- constructor->GetArtMethod(), shadow_frame->GetMethod())) {
+ ShouldBlockAccessToMember(constructor->GetArtMethod(), shadow_frame)) {
constructor = nullptr;
}
result->SetL(constructor);
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index 89eef88b88..90cac853ff 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -27,6 +27,7 @@
#include "base/logging.h" // For VLOG.
#include "base/macros.h"
#include "debugger.h"
+#include "dex/utf.h"
#include "jdwp/jdwp_constants.h"
#include "jdwp/jdwp_event.h"
#include "jdwp/jdwp_expand_buf.h"
@@ -34,7 +35,6 @@
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
-#include "utils.h"
namespace art {
diff --git a/runtime/jdwp_provider.h b/runtime/jdwp_provider.h
index b62e10b4f8..698fdc086d 100644
--- a/runtime/jdwp_provider.h
+++ b/runtime/jdwp_provider.h
@@ -28,6 +28,9 @@ enum class JdwpProvider {
kNone,
kInternal,
kAdbConnection,
+
+ // The current default provider
+ kDefaultJdwpProvider = kAdbConnection,
};
std::ostream& operator<<(std::ostream& os, const JdwpProvider& rhs);
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 12bf79d7ca..1baa613bb5 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -467,7 +467,7 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread,
// Fetch some data before looking up for an OSR method. We don't want thread
// suspension once we hold an OSR method, as the JIT code cache could delete the OSR
// method while we are being suspended.
- CodeItemDataAccessor accessor(method);
+ CodeItemDataAccessor accessor(method->DexInstructionData());
const size_t number_of_vregs = accessor.RegistersSize();
const char* shorty = method->GetShorty();
std::string method_name(VLOG_IS_ON(jit) ? method->PrettyMethod() : "");
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index e41667a5bf..7f0447732e 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -1699,7 +1699,9 @@ bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr
// can avoid a few expensive GenericJNI calls.
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
for (ArtMethod* m : data->GetMethods()) {
- instrumentation->UpdateMethodsCode(m, entrypoint);
+ // Call the dedicated method instead of the more generic UpdateMethodsCode, because
+ // `m` might be in the process of being deleted.
+ instrumentation->UpdateNativeMethodsCodeToJitCode(m, entrypoint);
}
if (collection_in_progress_) {
GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(data->GetCode()));
@@ -1823,8 +1825,10 @@ void JitCodeCache::FreeData(uint8_t* data) {
void JitCodeCache::Dump(std::ostream& os) {
MutexLock mu(Thread::Current(), lock_);
+ MutexLock mu2(Thread::Current(), g_jit_debug_mutex);
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 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/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 4bf2895723..de4d02edaf 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -775,8 +775,14 @@ bool ProfileCompilationInfo::ReadInlineCache(
for (; dex_classes_size > 0; dex_classes_size--) {
uint16_t type_index;
READ_UINT(uint16_t, buffer, type_index, error);
- dex_pc_data->AddClass(dex_profile_index_remap.Get(dex_profile_index),
- dex::TypeIndex(type_index));
+ auto it = dex_profile_index_remap.find(dex_profile_index);
+ if (it == dex_profile_index_remap.end()) {
+ // If we don't have an index that's because the dex file was filtered out when loading.
+ // Set missing types on the dex pc data.
+ dex_pc_data->SetIsMissingTypes();
+ } else {
+ dex_pc_data->AddClass(it->second, dex::TypeIndex(type_index));
+ }
}
}
}
@@ -1036,10 +1042,11 @@ ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::ReadProfileLin
// TODO(calin): Fix this API. ProfileCompilationInfo::Load should be static and
// return a unique pointer to a ProfileCompilationInfo upon success.
-bool ProfileCompilationInfo::Load(int fd, bool merge_classes) {
+bool ProfileCompilationInfo::Load(
+ int fd, bool merge_classes, const ProfileLoadFilterFn& filter_fn) {
std::string error;
- ProfileLoadStatus status = LoadInternal(fd, &error, merge_classes);
+ ProfileLoadStatus status = LoadInternal(fd, &error, merge_classes, filter_fn);
if (status == kProfileLoadSuccess) {
return true;
@@ -1245,7 +1252,10 @@ bool ProfileCompilationInfo::ProfileSource::HasEmptyContent() const {
// TODO(calin): fail fast if the dex checksums don't match.
ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::LoadInternal(
- int32_t fd, std::string* error, bool merge_classes) {
+ int32_t fd,
+ std::string* error,
+ bool merge_classes,
+ const ProfileLoadFilterFn& filter_fn) {
ScopedTrace trace(__PRETTY_FUNCTION__);
DCHECK_GE(fd, 0);
@@ -1327,20 +1337,29 @@ ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::LoadInternal(
}
SafeMap<uint8_t, uint8_t> dex_profile_index_remap;
- if (!RemapProfileIndex(profile_line_headers, &dex_profile_index_remap)) {
+ if (!RemapProfileIndex(profile_line_headers, filter_fn, &dex_profile_index_remap)) {
return kProfileLoadBadData;
}
for (uint8_t k = 0; k < number_of_dex_files; k++) {
- // Now read the actual profile line.
- status = ReadProfileLine(uncompressed_data,
- number_of_dex_files,
- profile_line_headers[k],
- dex_profile_index_remap,
- merge_classes,
- error);
- if (status != kProfileLoadSuccess) {
- return status;
+ if (!filter_fn(profile_line_headers[k].dex_location, profile_line_headers[k].checksum)) {
+ // We have to skip the line. Advanced the current pointer of the buffer.
+ size_t profile_line_size =
+ profile_line_headers[k].class_set_size +
+ profile_line_headers[k].method_region_size_bytes +
+ DexFileData::ComputeBitmapStorage(profile_line_headers[k].num_method_ids);
+ uncompressed_data.Advance(profile_line_size);
+ } else {
+ // Now read the actual profile line.
+ status = ReadProfileLine(uncompressed_data,
+ number_of_dex_files,
+ profile_line_headers[k],
+ dex_profile_index_remap,
+ merge_classes,
+ error);
+ if (status != kProfileLoadSuccess) {
+ return status;
+ }
}
}
@@ -1355,12 +1374,16 @@ ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::LoadInternal(
bool ProfileCompilationInfo::RemapProfileIndex(
const std::vector<ProfileLineHeader>& profile_line_headers,
+ const ProfileLoadFilterFn& filter_fn,
/*out*/SafeMap<uint8_t, uint8_t>* dex_profile_index_remap) {
// First verify that all checksums match. This will avoid adding garbage to
// the current profile info.
// Note that the number of elements should be very small, so this should not
// be a performance issue.
for (const ProfileLineHeader other_profile_line_header : profile_line_headers) {
+ if (!filter_fn(other_profile_line_header.dex_location, other_profile_line_header.checksum)) {
+ continue;
+ }
// verify_checksum is false because we want to differentiate between a missing dex data and
// a mismatched checksum.
const DexFileData* dex_data = FindDexData(other_profile_line_header.dex_location,
@@ -1374,6 +1397,9 @@ bool ProfileCompilationInfo::RemapProfileIndex(
// All checksums match. Import the data.
uint32_t num_dex_files = static_cast<uint32_t>(profile_line_headers.size());
for (uint32_t i = 0; i < num_dex_files; i++) {
+ if (!filter_fn(profile_line_headers[i].dex_location, profile_line_headers[i].checksum)) {
+ continue;
+ }
const DexFileData* dex_data = GetOrAddDexFileData(profile_line_headers[i].dex_location,
profile_line_headers[i].checksum,
profile_line_headers[i].num_method_ids);
@@ -2054,4 +2080,10 @@ bool ProfileCompilationInfo::UpdateProfileKeys(
return true;
}
+bool ProfileCompilationInfo::ProfileFilterFnAcceptAll(
+ const std::string& dex_location ATTRIBUTE_UNUSED,
+ uint32_t checksum ATTRIBUTE_UNUSED) {
+ return true;
+}
+
} // namespace art
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index 350ce9ed8d..1973f3f09e 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -306,7 +306,19 @@ class ProfileCompilationInfo {
// Load or Merge profile information from the given file descriptor.
// If the current profile is non-empty the load will fail.
// If merge_classes is set to false, classes will not be merged/loaded.
- bool Load(int fd, bool merge_classes = true);
+ // If filter_fn is present, it will be used to filter out profile data belonging
+ // to dex file which do not comply with the filter
+ // (i.e. for which filter_fn(dex_location, dex_checksum) is false).
+ using ProfileLoadFilterFn = std::function<bool(const std::string&, uint32_t)>;
+ // Profile filter method which accepts all dex locations.
+ // This is convenient to use when we need to accept all locations without repeating the same
+ // lambda.
+ static bool ProfileFilterFnAcceptAll(const std::string& dex_location, uint32_t checksum);
+
+ bool Load(
+ int fd,
+ bool merge_classes = true,
+ const ProfileLoadFilterFn& filter_fn = ProfileFilterFnAcceptAll);
// Verify integrity of the profile file with the provided dex files.
// If there exists a DexData object which maps to a dex_file, then it verifies that:
@@ -459,14 +471,21 @@ class ProfileCompilationInfo {
class_set(std::less<dex::TypeIndex>(), allocator->Adapter(kArenaAllocProfile)),
num_method_ids(num_methods),
bitmap_storage(allocator->Adapter(kArenaAllocProfile)) {
- const size_t num_bits = num_method_ids * kBitmapIndexCount;
- bitmap_storage.resize(RoundUp(num_bits, kBitsPerByte) / kBitsPerByte);
+ bitmap_storage.resize(ComputeBitmapStorage(num_method_ids));
if (!bitmap_storage.empty()) {
method_bitmap =
- BitMemoryRegion(MemoryRegion(&bitmap_storage[0], bitmap_storage.size()), 0, num_bits);
+ BitMemoryRegion(MemoryRegion(
+ &bitmap_storage[0], bitmap_storage.size()), 0, ComputeBitmapBits(num_method_ids));
}
}
+ static size_t ComputeBitmapBits(uint32_t num_method_ids) {
+ return num_method_ids * kBitmapIndexCount;
+ }
+ static size_t ComputeBitmapStorage(uint32_t num_method_ids) {
+ return RoundUp(ComputeBitmapBits(num_method_ids), kBitsPerByte) / kBitsPerByte;
+ }
+
bool operator==(const DexFileData& other) const {
return checksum == other.checksum && method_map == other.method_map;
}
@@ -689,10 +708,12 @@ class ProfileCompilationInfo {
/*out*/ std::unique_ptr<ProfileSource>* source,
/*out*/ std::string* error);
- // Entry point for profile loding functionality.
- ProfileLoadStatus LoadInternal(int32_t fd,
- std::string* error,
- bool merge_classes = true);
+ // Entry point for profile loading functionality.
+ ProfileLoadStatus LoadInternal(
+ int32_t fd,
+ std::string* error,
+ bool merge_classes = true,
+ const ProfileLoadFilterFn& filter_fn = ProfileFilterFnAcceptAll);
// Read the profile header from the given fd and store the number of profile
// lines into number_of_dex_files.
@@ -736,6 +757,7 @@ class ProfileCompilationInfo {
// The method generates mapping of profile indices while merging a new profile
// data into current data. It returns true, if the mapping was successful.
bool RemapProfileIndex(const std::vector<ProfileLineHeader>& profile_line_headers,
+ const ProfileLoadFilterFn& filter_fn,
/*out*/SafeMap<uint8_t, uint8_t>* dex_profile_index_remap);
// Read the inline cache encoding from line_bufer into inline_cache.
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index b4265d1a28..4ac11ee422 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -314,6 +314,10 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest {
}
}
+ bool IsEmpty(const ProfileCompilationInfo& info) {
+ return info.IsEmpty();
+ }
+
// Cannot sizeof the actual arrays so hard code the values here.
// They should not change anyway.
static constexpr int kProfileMagicSize = 4;
@@ -1124,4 +1128,169 @@ TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyFail) {
ASSERT_FALSE(info.UpdateProfileKeys(dex_files));
}
+TEST_F(ProfileCompilationInfoTest, FilteredLoading) {
+ ScratchFile profile;
+
+ ProfileCompilationInfo saved_info;
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
+
+ // Add methods with inline caches.
+ for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
+ // Add a method which is part of the same dex file as one of the class from the inline caches.
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, method_idx, pmi, &saved_info));
+ // Add a method which is outside the set of dex files.
+ ASSERT_TRUE(AddMethod("dex_location4", /* checksum */ 4, method_idx, pmi, &saved_info));
+ }
+
+ ASSERT_TRUE(saved_info.Save(GetFd(profile)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ // Check that we get back what we saved.
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+
+ // Filter out dex locations. Keep only dex_location1 and dex_location2.
+ ProfileCompilationInfo::ProfileLoadFilterFn filter_fn =
+ [](const std::string& dex_location, uint32_t checksum) -> bool {
+ return (dex_location == "dex_location1" && checksum == 1)
+ || (dex_location == "dex_location3" && checksum == 3);
+ };
+ ASSERT_TRUE(loaded_info.Load(GetFd(profile), true, filter_fn));
+
+ // Verify that we filtered out locations during load.
+
+ // Dex location 2 and 4 should have been filtered out
+ for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
+ ASSERT_TRUE(nullptr == loaded_info.GetMethod("dex_location2", /* checksum */ 2, method_idx));
+ ASSERT_TRUE(nullptr == loaded_info.GetMethod("dex_location4", /* checksum */ 4, method_idx));
+ }
+
+ // Dex location 1 should have all all the inline caches referencing dex location 2 set to
+ // missing types.
+ for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
+ // The methods for dex location 1 should be in the profile data.
+ std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
+ loaded_info.GetMethod("dex_location1", /* checksum */ 1, /* method_idx */ method_idx);
+ ASSERT_TRUE(loaded_pmi1 != nullptr);
+
+ // Verify the inline cache.
+ // Everything should be as constructed by GetOfflineProfileMethodInfo with the exception
+ // of the inline caches referring types from dex_location2.
+ // These should be set to IsMissingType.
+ ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
+
+ // Monomorphic types should remain the same as dex_location1 was kept.
+ for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
+ ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
+ dex_pc_data.AddClass(0, dex::TypeIndex(0));
+ ic_map->Put(dex_pc, dex_pc_data);
+ }
+ // Polymorphic inline cache should have been transformed to IsMissingType due to
+ // the removal of dex_location2.
+ for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
+ ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
+ dex_pc_data.SetIsMissingTypes();
+ ic_map->Put(dex_pc, dex_pc_data);
+ }
+
+ // Megamorphic are not affected by removal of dex files.
+ for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
+ ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
+ dex_pc_data.SetIsMegamorphic();
+ ic_map->Put(dex_pc, dex_pc_data);
+ }
+ // Missing types are not affected be removal of dex files.
+ for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
+ ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
+ dex_pc_data.SetIsMissingTypes();
+ ic_map->Put(dex_pc, dex_pc_data);
+ }
+
+ ProfileCompilationInfo::OfflineProfileMethodInfo expected_pmi(ic_map);
+
+ // The dex references should not have dex_location2 in the list.
+ expected_pmi.dex_references.emplace_back("dex_location1", /* checksum */1, kMaxMethodIds);
+ expected_pmi.dex_references.emplace_back("dex_location3", /* checksum */3, kMaxMethodIds);
+
+ // Now check that we get back what we expect.
+ ASSERT_TRUE(*loaded_pmi1 == expected_pmi);
+ }
+}
+
+TEST_F(ProfileCompilationInfoTest, FilteredLoadingRemoveAll) {
+ ScratchFile profile;
+
+ ProfileCompilationInfo saved_info;
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
+
+ // Add methods with inline caches.
+ for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
+ // Add a method which is part of the same dex file as one of the class from the inline caches.
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, method_idx, pmi, &saved_info));
+ // Add a method which is outside the set of dex files.
+ ASSERT_TRUE(AddMethod("dex_location4", /* checksum */ 4, method_idx, pmi, &saved_info));
+ }
+
+ ASSERT_TRUE(saved_info.Save(GetFd(profile)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ // Check that we get back what we saved.
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+
+ // Remove all elements.
+ ProfileCompilationInfo::ProfileLoadFilterFn filter_fn =
+ [](const std::string&, uint32_t) -> bool { return false; };
+ ASSERT_TRUE(loaded_info.Load(GetFd(profile), true, filter_fn));
+
+ // Verify that we filtered out everything.
+ ASSERT_TRUE(IsEmpty(loaded_info));
+}
+
+TEST_F(ProfileCompilationInfoTest, FilteredLoadingKeepAll) {
+ ScratchFile profile;
+
+ ProfileCompilationInfo saved_info;
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
+
+ // Add methods with inline caches.
+ for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
+ // Add a method which is part of the same dex file as one of the
+ // class from the inline caches.
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
+ // Add a method which is outside the set of dex files.
+ ASSERT_TRUE(AddMethod("dex_location4", /* checksum */ 4, method_idx, pmi, &saved_info));
+ }
+
+ ASSERT_TRUE(saved_info.Save(GetFd(profile)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ // Check that we get back what we saved.
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+
+ // Keep all elements.
+ ProfileCompilationInfo::ProfileLoadFilterFn filter_fn =
+ [](const std::string&, uint32_t) -> bool { return true; };
+ ASSERT_TRUE(loaded_info.Load(GetFd(profile), true, filter_fn));
+
+
+ ASSERT_TRUE(loaded_info.Equals(saved_info));
+
+ for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
+ std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
+ loaded_info.GetMethod("dex_location1", /* checksum */ 1, method_idx);
+ ASSERT_TRUE(loaded_pmi1 != nullptr);
+ ASSERT_TRUE(*loaded_pmi1 == pmi);
+ }
+ for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
+ std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi2 =
+ loaded_info.GetMethod("dex_location4", /* checksum */ 4, method_idx);
+ ASSERT_TRUE(loaded_pmi2 != nullptr);
+ ASSERT_TRUE(*loaded_pmi2 == pmi);
+ }
+}
+
} // namespace art
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index c40360f612..666fb98354 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -33,6 +33,7 @@
#include "base/stl_util.h"
#include "class_linker-inl.h"
#include "dex/dex_file-inl.h"
+#include "dex/utf.h"
#include "fault_handler.h"
#include "hidden_api.h"
#include "gc/accounting/card_table-inl.h"
@@ -57,7 +58,6 @@
#include "safe_map.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
-#include "utf.h"
#include "well_known_classes.h"
namespace {
@@ -80,17 +80,28 @@ namespace art {
// things not rendering correctly. E.g. b/16858794
static constexpr bool kWarnJniAbort = false;
-// Helpers to check if we need to warn about accessing hidden API fields and to call instrumentation
-// functions for them. These take jobjects so we don't need to set up handles for the rare case
-// where these actually do something. Once these functions return it is possible there will be
-// a pending exception if the instrumentation happens to throw one.
+static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Class> klass = GetCallingClass(self, /* num_frames */ 1);
+ // If `klass` is null, it is an unattached native thread. Assume this is
+ // *not* boot class path.
+ return klass != nullptr && klass->IsBootStrapClassLoaded();
+}
+
+template<typename T>
+ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return hiddenapi::ShouldBlockAccessToMember(member, self, IsCallerInBootClassPath);
+}
+
+// Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set
+// up handles for the rare case where these actually do something. Once these functions return it is
+// possible there will be a pending exception if the instrumentation happens to throw one.
static void NotifySetObjectField(ArtField* field, jobject obj, jobject jval)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK_EQ(field->GetTypeAsPrimitiveType(), Primitive::kPrimNot);
- Thread* self = Thread::Current();
- hiddenapi::MaybeWarnAboutMemberAccess(field, self, /* num_frames */ 1);
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (UNLIKELY(instrumentation->HasFieldWriteListeners())) {
+ Thread* self = Thread::Current();
ArtMethod* cur_method = self->GetCurrentMethod(/*dex_pc*/ nullptr,
/*check_suspended*/ true,
/*abort_on_error*/ false);
@@ -115,10 +126,9 @@ static void NotifySetObjectField(ArtField* field, jobject obj, jobject jval)
static void NotifySetPrimitiveField(ArtField* field, jobject obj, JValue val)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK_NE(field->GetTypeAsPrimitiveType(), Primitive::kPrimNot);
- Thread* self = Thread::Current();
- hiddenapi::MaybeWarnAboutMemberAccess(field, self, /* num_frames */ 1);
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (UNLIKELY(instrumentation->HasFieldWriteListeners())) {
+ Thread* self = Thread::Current();
ArtMethod* cur_method = self->GetCurrentMethod(/*dex_pc*/ nullptr,
/*check_suspended*/ true,
/*abort_on_error*/ false);
@@ -140,10 +150,9 @@ static void NotifySetPrimitiveField(ArtField* field, jobject obj, JValue val)
static void NotifyGetField(ArtField* field, jobject obj)
REQUIRES_SHARED(Locks::mutator_lock_) {
- Thread* self = Thread::Current();
- hiddenapi::MaybeWarnAboutMemberAccess(field, self, /* num_frames */ 1);
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (UNLIKELY(instrumentation->HasFieldReadListeners())) {
+ Thread* self = Thread::Current();
ArtMethod* cur_method = self->GetCurrentMethod(/*dex_pc*/ nullptr,
/*check_suspended*/ true,
/*abort_on_error*/ false);
@@ -243,8 +252,7 @@ static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class,
} else {
method = c->FindClassMethod(name, sig, pointer_size);
}
- if (method != nullptr &&
- hiddenapi::ShouldBlockAccessToMember(method, soa.Self(), /* num_frames */ 1)) {
+ if (method != nullptr && ShouldBlockAccessToMember(method, soa.Self())) {
method = nullptr;
}
if (method == nullptr || method->IsStatic() != is_static) {
@@ -323,8 +331,7 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con
} else {
field = c->FindInstanceField(name, field_type->GetDescriptor(&temp));
}
- if (field != nullptr &&
- hiddenapi::ShouldBlockAccessToMember(field, soa.Self(), /* num_frames */ 1)) {
+ if (field != nullptr && ShouldBlockAccessToMember(field, soa.Self())) {
field = nullptr;
}
if (field == nullptr) {
diff --git a/runtime/leb128.h b/runtime/leb128.h
index 9fb09d8fc2..07eadc1ddf 100644
--- a/runtime/leb128.h
+++ b/runtime/leb128.h
@@ -55,6 +55,10 @@ static inline uint32_t DecodeUnsignedLeb128(const uint8_t** data) {
return static_cast<uint32_t>(result);
}
+static inline uint32_t DecodeUnsignedLeb128WithoutMovingCursor(const uint8_t* data) {
+ return DecodeUnsignedLeb128(&data);
+}
+
static inline bool DecodeUnsignedLeb128Checked(const uint8_t** data,
const void* end,
uint32_t* out) {
@@ -203,6 +207,34 @@ static inline uint32_t UnsignedLeb128Size(uint32_t data) {
return (x * 37) >> 8;
}
+static inline bool IsLeb128Terminator(const uint8_t* ptr) {
+ return *ptr <= 0x7f;
+}
+
+// Returns the first byte of a Leb128 value assuming that:
+// (1) `end_ptr` points to the first byte after the Leb128 value, and
+// (2) there is another Leb128 value before this one.
+template <typename T>
+static inline T* ReverseSearchUnsignedLeb128(T* end_ptr) {
+ static_assert(std::is_same<typename std::remove_const<T>::type, uint8_t>::value,
+ "T must be a uint8_t");
+ T* ptr = end_ptr;
+
+ // Move one byte back, check that this is the terminating byte.
+ ptr--;
+ DCHECK(IsLeb128Terminator(ptr));
+
+ // Keep moving back while the previous byte is not a terminating byte.
+ // Fail after reading five bytes in case there isn't another Leb128 value
+ // before this one.
+ while (!IsLeb128Terminator(ptr - 1)) {
+ ptr--;
+ DCHECK_LE(static_cast<ptrdiff_t>(end_ptr - ptr), 5);
+ }
+
+ return ptr;
+}
+
// Returns the number of bytes needed to encode the value in unsigned LEB128.
static inline uint32_t SignedLeb128Size(int32_t data) {
// Like UnsignedLeb128Size(), but we need one bit beyond the highest bit that differs from sign.
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 88f30a8900..2701ec66a4 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -231,7 +231,7 @@ bool ConvertJValueCommon(
StackHandleScope<2> hs(Thread::Current());
Handle<mirror::Class> h_to(hs.NewHandle(to));
Handle<mirror::Object> h_obj(hs.NewHandle(src_value.GetL()));
- if (h_obj != nullptr && !to->IsAssignableFrom(h_obj->GetClass())) {
+ if (UNLIKELY(!h_obj.IsNull() && !to->IsAssignableFrom(h_obj->GetClass()))) {
ThrowClassCastException(h_to.Get(), h_obj->GetClass());
return false;
}
@@ -246,7 +246,7 @@ bool ConvertJValueCommon(
Primitive::Type type;
if (!GetUnboxedPrimitiveType(to, &type)) {
ObjPtr<mirror::Class> boxed_from_class = GetBoxedPrimitiveClass(from_type);
- if (boxed_from_class->IsSubClass(to)) {
+ if (LIKELY(boxed_from_class->IsSubClass(to))) {
type = from_type;
} else {
ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
@@ -259,7 +259,7 @@ bool ConvertJValueCommon(
return false;
}
- if (!ConvertPrimitiveValueNoThrow(from_type, type, src_value, value)) {
+ if (UNLIKELY(!ConvertPrimitiveValueNoThrow(from_type, type, src_value, value))) {
ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
return false;
}
@@ -274,7 +274,7 @@ bool ConvertJValueCommon(
DCHECK(IsPrimitiveType(to_type));
ObjPtr<mirror::Object> from_obj(src_value.GetL());
- if (UNLIKELY(from_obj == nullptr)) {
+ if (UNLIKELY(from_obj.IsNull())) {
ThrowNullPointerException(
StringPrintf("Expected to unbox a '%s' primitive type but was returned null",
from->PrettyDescriptor().c_str()).c_str());
@@ -289,7 +289,14 @@ bool ConvertJValueCommon(
}
if (UNLIKELY(!ConvertPrimitiveValueNoThrow(unboxed_type, to_type, unboxed_value, value))) {
- ThrowClassCastException(from, to);
+ if (from->IsAssignableFrom(GetBoxedPrimitiveClass(to_type))) {
+ // CallSite may be Number, but the Number object is
+ // incompatible, e.g. Number (Integer) for a short.
+ ThrowClassCastException(from, to);
+ } else {
+ // CallSite is incompatible, e.g. Integer for a short.
+ ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
+ }
return false;
}
@@ -408,7 +415,7 @@ static inline bool MethodHandleInvokeMethod(ArtMethod* called_method,
const InstructionOperands* const operands,
JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
// Compute method information.
- CodeItemDataAccessor accessor(called_method);
+ CodeItemDataAccessor accessor(called_method->DexInstructionData());
// Number of registers for the callee's call frame. Note that for non-exact
// invokes, we always derive this information from the callee method. We
@@ -550,7 +557,7 @@ static inline bool MethodHandleInvokeTransform(ArtMethod* called_method,
// - One for the only method argument (an EmulatedStackFrame).
static constexpr size_t kNumRegsForTransform = 2;
- CodeItemDataAccessor accessor(called_method);
+ CodeItemDataAccessor accessor(called_method->DexInstructionData());
DCHECK_EQ(kNumRegsForTransform, accessor.RegistersSize());
DCHECK_EQ(kNumRegsForTransform, accessor.InsSize());
@@ -1034,7 +1041,7 @@ static inline bool MethodHandleInvokeExactInternal(
}
// Compute method information.
- CodeItemDataAccessor accessor(called_method);
+ CodeItemDataAccessor accessor(called_method->DexInstructionData());
uint16_t num_regs;
size_t num_input_regs;
size_t first_dest_reg;
diff --git a/runtime/method_handles_test.cc b/runtime/method_handles_test.cc
new file mode 100644
index 0000000000..a9473421cb
--- /dev/null
+++ b/runtime/method_handles_test.cc
@@ -0,0 +1,382 @@
+/*
+ * 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.
+ */
+
+#include "method_handles.h"
+
+#include "class_linker-inl.h"
+#include "common_runtime_test.h"
+#include "handle_scope-inl.h"
+#include "jvalue-inl.h"
+#include "mirror/method_type.h"
+#include "mirror/object_array-inl.h"
+#include "reflection.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-current-inl.h"
+
+namespace art {
+
+namespace {
+ bool IsClassCastException(ObjPtr<mirror::Throwable> throwable)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return throwable->GetClass()->DescriptorEquals("Ljava/lang/ClassCastException;");
+ }
+
+ bool IsNullPointerException(ObjPtr<mirror::Throwable> throwable)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return throwable->GetClass()->DescriptorEquals("Ljava/lang/NullPointerException;");
+ }
+
+ bool IsWrongMethodTypeException(ObjPtr<mirror::Throwable> throwable)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return throwable->GetClass()->DescriptorEquals("Ljava/lang/invoke/WrongMethodTypeException;");
+ }
+
+ static mirror::MethodType* CreateVoidMethodType(Thread* self,
+ Handle<mirror::Class> parameter_type)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ StackHandleScope<2> hs(self);
+ ObjPtr<mirror::Class> class_type = mirror::Class::GetJavaLangClass();
+ ObjPtr<mirror::Class> class_array_type = cl->FindArrayClass(self, &class_type);
+ auto parameter_types = hs.NewHandle(
+ mirror::ObjectArray<mirror::Class>::Alloc(self, class_array_type, 1));
+ parameter_types->Set(0, parameter_type.Get());
+ Handle<mirror::Class> void_class = hs.NewHandle(cl->FindPrimitiveClass('V'));
+ return mirror::MethodType::Create(self, void_class, parameter_types);
+ }
+
+ static bool TryConversion(Thread* self,
+ Handle<mirror::Class> from,
+ Handle<mirror::Class> to,
+ JValue* value)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ StackHandleScope<2> hs(self);
+ Handle<mirror::MethodType> from_mt = hs.NewHandle(CreateVoidMethodType(self, from));
+ Handle<mirror::MethodType> to_mt = hs.NewHandle(CreateVoidMethodType(self, to));
+ return ConvertJValueCommon(from_mt, to_mt, from.Get(), to.Get(), value);
+ }
+} // namespace
+
+class MethodHandlesTest : public CommonRuntimeTest {};
+
+//
+// Primitive -> Primitive Conversions
+//
+
+TEST_F(MethodHandlesTest, SupportedPrimitiveWideningBI) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('B'));
+ Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('I'));
+ JValue value = JValue::FromPrimitive(static_cast<int8_t>(3));
+ ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value));
+ ASSERT_EQ(3, value.GetI());
+ ASSERT_FALSE(soa.Self()->IsExceptionPending());
+}
+
+TEST_F(MethodHandlesTest, SupportedPrimitiveWideningCJ) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('C'));
+ Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('J'));
+ uint16_t raw_value = 0x8000;
+ JValue value = JValue::FromPrimitive(raw_value);
+ ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value));
+ ASSERT_FALSE(soa.Self()->IsExceptionPending());
+ ASSERT_EQ(static_cast<int64_t>(raw_value), value.GetJ());
+}
+
+TEST_F(MethodHandlesTest, SupportedPrimitiveWideningIF) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I'));
+ Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('F'));
+ JValue value = JValue::FromPrimitive(-16);
+ ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value));
+ ASSERT_FALSE(soa.Self()->IsExceptionPending());
+ ASSERT_FLOAT_EQ(-16.0f, value.GetF());
+}
+
+TEST_F(MethodHandlesTest, UnsupportedPrimitiveWideningBC) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('B'));
+ Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('C'));
+ JValue value;
+ value.SetB(0);
+ ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+ ASSERT_TRUE(soa.Self()->IsExceptionPending());
+ ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException()));
+ soa.Self()->ClearException();
+}
+
+TEST_F(MethodHandlesTest, UnsupportedPrimitiveWideningSC) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('S'));
+ Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('C'));
+ JValue value;
+ value.SetS(0x1234);
+ ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+ ASSERT_TRUE(soa.Self()->IsExceptionPending());
+ ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException()));
+ soa.Self()->ClearException();
+}
+
+TEST_F(MethodHandlesTest, UnsupportedPrimitiveWideningDJ) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('D'));
+ Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('J'));
+ JValue value;
+ value.SetD(1e72);
+ ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+ ASSERT_TRUE(soa.Self()->IsExceptionPending());
+ ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException()));
+ soa.Self()->ClearException();
+}
+
+TEST_F(MethodHandlesTest, UnsupportedPrimitiveWideningZI) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('Z'));
+ Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('I'));
+ JValue value;
+ value.SetZ(true);
+ ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+ ASSERT_TRUE(soa.Self()->IsExceptionPending());
+ ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException()));
+ soa.Self()->ClearException();
+}
+
+//
+// Reference -> Reference Conversions
+//
+
+TEST_F(MethodHandlesTest, SupportedReferenceCast) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ StackHandleScope<3> hs(soa.Self());
+ static const int32_t kInitialValue = 101;
+ JValue value = JValue::FromPrimitive(kInitialValue);
+ Handle<mirror::Object> boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value).Ptr());
+ Handle<mirror::Class> from = hs.NewHandle(boxed_value->GetClass());
+ Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Number;"));
+ value.SetL(boxed_value.Get());
+ ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value));
+ ASSERT_FALSE(soa.Self()->IsExceptionPending());
+ JValue unboxed_value;
+ ASSERT_TRUE(UnboxPrimitiveForResult(value.GetL(), cl->FindPrimitiveClass('I'), &unboxed_value));
+ ASSERT_EQ(kInitialValue, unboxed_value.GetI());
+}
+
+TEST_F(MethodHandlesTest, UnsupportedReferenceCast) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ StackHandleScope<3> hs(soa.Self());
+ JValue value = JValue::FromPrimitive(3.733e2);
+ Handle<mirror::Object> boxed_value =
+ hs.NewHandle(BoxPrimitive(Primitive::kPrimDouble, value).Ptr());
+ Handle<mirror::Class> from = hs.NewHandle(boxed_value->GetClass());
+ Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;"));
+ value.SetL(boxed_value.Get());
+ ASSERT_FALSE(soa.Self()->IsExceptionPending());
+ ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+ ASSERT_TRUE(soa.Self()->IsExceptionPending());
+ ASSERT_TRUE(IsClassCastException(soa.Self()->GetException()));
+ soa.Self()->ClearException();
+}
+
+//
+// Primitive -> Reference Conversions
+//
+
+TEST_F(MethodHandlesTest, SupportedPrimitiveConversionPrimitiveToBoxed) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ StackHandleScope<2> hs(soa.Self());
+ const int32_t kInitialValue = 1;
+ JValue value = JValue::FromPrimitive(kInitialValue);
+ Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I'));
+ Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;"));
+ ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value));
+ ASSERT_FALSE(soa.Self()->IsExceptionPending());
+ JValue unboxed_to_value;
+ ASSERT_TRUE(UnboxPrimitiveForResult(value.GetL(), from.Get(), &unboxed_to_value));
+ ASSERT_EQ(kInitialValue, unboxed_to_value.GetI());
+}
+
+TEST_F(MethodHandlesTest, SupportedPrimitiveConversionPrimitiveToBoxedSuper) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ StackHandleScope<2> hs(soa.Self());
+ const int32_t kInitialValue = 1;
+ JValue value = JValue::FromPrimitive(kInitialValue);
+ Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I'));
+ Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Number;"));
+ ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value));
+ ASSERT_FALSE(soa.Self()->IsExceptionPending());
+ JValue unboxed_to_value;
+ ASSERT_TRUE(UnboxPrimitiveForResult(value.GetL(), from.Get(), &unboxed_to_value));
+ ASSERT_EQ(kInitialValue, unboxed_to_value.GetI());
+}
+
+TEST_F(MethodHandlesTest, UnsupportedPrimitiveConversionNotBoxable) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ StackHandleScope<2> hs(soa.Self());
+ const int32_t kInitialValue = 1;
+ JValue value = JValue::FromPrimitive(kInitialValue);
+ Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I'));
+ Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Runtime;"));
+ ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+ ASSERT_TRUE(soa.Self()->IsExceptionPending());
+ ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException()));
+ soa.Self()->ClearException();
+}
+
+TEST_F(MethodHandlesTest, UnsupportedPrimitiveConversionPrimitiveToBoxedWider) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ StackHandleScope<2> hs(soa.Self());
+ const int32_t kInitialValue = 1;
+ JValue value = JValue::FromPrimitive(kInitialValue);
+ Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I'));
+ Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Long;"));
+ ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+ ASSERT_TRUE(soa.Self()->IsExceptionPending());
+ ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException()));
+ soa.Self()->ClearException();
+}
+
+TEST_F(MethodHandlesTest, UnsupportedPrimitiveConversionPrimitiveToBoxedNarrower) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ StackHandleScope<2> hs(soa.Self());
+ const int32_t kInitialValue = 1;
+ JValue value = JValue::FromPrimitive(kInitialValue);
+ Handle<mirror::Class> from = hs.NewHandle(cl->FindPrimitiveClass('I'));
+ Handle<mirror::Class> to = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Byte;"));
+ ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+ ASSERT_TRUE(soa.Self()->IsExceptionPending());
+ ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException()));
+ soa.Self()->ClearException();
+}
+
+//
+// Reference -> Primitive Conversions
+//
+
+TEST_F(MethodHandlesTest, SupportedBoxedToPrimitiveConversion) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ StackHandleScope<3> hs(soa.Self());
+ const int32_t kInitialValue = 101;
+ JValue value = JValue::FromPrimitive(kInitialValue);
+ Handle<mirror::Object> boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value).Ptr());
+ Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;"));
+ Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('I'));
+ value.SetL(boxed_value.Get());
+ ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value));
+ ASSERT_FALSE(soa.Self()->IsExceptionPending());
+ ASSERT_EQ(kInitialValue, value.GetI());
+}
+
+TEST_F(MethodHandlesTest, SupportedBoxedToWiderPrimitiveConversion) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ StackHandleScope<3> hs(soa.Self());
+ static const int32_t kInitialValue = 101;
+ JValue value = JValue::FromPrimitive(kInitialValue);
+ Handle<mirror::Object> boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value).Ptr());
+ Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;"));
+ Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('J'));
+ value.SetL(boxed_value.Get());
+ ASSERT_TRUE(TryConversion(soa.Self(), from, to, &value));
+ ASSERT_EQ(kInitialValue, value.GetJ());
+}
+
+TEST_F(MethodHandlesTest, UnsupportedNullBoxedToPrimitiveConversion) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ StackHandleScope<3> hs(soa.Self());
+ JValue value = JValue::FromPrimitive(101);
+ ScopedNullHandle<mirror::Object> boxed_value;
+ Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;"));
+ Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('I'));
+ value.SetL(boxed_value.Get());
+ ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+ ASSERT_TRUE(soa.Self()->IsExceptionPending());
+ ASSERT_TRUE(IsNullPointerException(soa.Self()->GetException()));
+ soa.Self()->ClearException();
+}
+
+TEST_F(MethodHandlesTest, UnsupportedNotBoxReferenceToPrimitiveConversion) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Class;"));
+ Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('I'));
+ // Set value to be converted as some non-primitive type.
+ JValue value;
+ value.SetL(cl->FindPrimitiveClass('V'));
+ ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+ ASSERT_TRUE(soa.Self()->IsExceptionPending());
+ ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException()));
+ soa.Self()->ClearException();
+}
+
+TEST_F(MethodHandlesTest, UnsupportedBoxedToNarrowerPrimitiveConversionNoCast) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ StackHandleScope<3> hs(soa.Self());
+ static const int32_t kInitialValue = 101;
+ JValue value = JValue::FromPrimitive(kInitialValue);
+ Handle<mirror::Object> boxed_value = hs.NewHandle(BoxPrimitive(Primitive::kPrimInt, value).Ptr());
+ Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Integer;"));
+ Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('S'));
+ value.SetL(boxed_value.Get());
+ ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+ ASSERT_TRUE(soa.Self()->IsExceptionPending());
+ ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException()));
+ soa.Self()->ClearException();
+}
+
+TEST_F(MethodHandlesTest, UnsupportedBoxedToNarrowerPrimitiveConversionWithCast) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ StackHandleScope<3> hs(soa.Self());
+ static const double kInitialValue = 1e77;
+ JValue value = JValue::FromPrimitive(kInitialValue);
+ Handle<mirror::Object> boxed_value =
+ hs.NewHandle(BoxPrimitive(Primitive::kPrimDouble, value).Ptr());
+ Handle<mirror::Class> from = hs.NewHandle(cl->FindSystemClass(soa.Self(), "Ljava/lang/Number;"));
+ Handle<mirror::Class> to = hs.NewHandle(cl->FindPrimitiveClass('F'));
+ value.SetL(boxed_value.Get());
+ ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
+ ASSERT_TRUE(soa.Self()->IsExceptionPending());
+ ASSERT_TRUE(IsClassCastException(soa.Self()->GetException()));
+ soa.Self()->ClearException();
+}
+
+} // namespace art
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index 24c75ec0d8..8c2a49c5f6 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -24,11 +24,11 @@
#include "base/bit_utils.h"
#include "class.h"
#include "common_throws.h"
+#include "dex/utf.h"
#include "gc/heap-inl.h"
#include "globals.h"
#include "runtime.h"
#include "thread.h"
-#include "utf.h"
#include "utils.h"
namespace art {
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc
index 82ff6ddead..cad84ceecb 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -21,6 +21,7 @@
#include "base/array_ref.h"
#include "base/stl_util.h"
#include "class-inl.h"
+#include "dex/utf-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "gc_root-inl.h"
#include "handle_scope-inl.h"
@@ -29,7 +30,6 @@
#include "runtime.h"
#include "string-inl.h"
#include "thread.h"
-#include "utf-inl.h"
namespace art {
namespace mirror {
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 325591fb53..0c9c65a401 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -1378,7 +1378,7 @@ void Monitor::VisitLocks(StackVisitor* stack_visitor, void (*callback)(mirror::O
// Is there any reason to believe there's any synchronization in this method?
CHECK(m->GetCodeItem() != nullptr) << m->PrettyMethod();
- CodeItemDataAccessor accessor(m);
+ CodeItemDataAccessor accessor(m->DexInstructionData());
if (accessor.TriesSize() == 0) {
return; // No "tries" implies no synchronization, so no held locks to report.
}
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 2892967a51..e58fd9dac9 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -174,6 +174,7 @@ enum {
DISABLE_VERIFIER = 1 << 9,
ONLY_USE_SYSTEM_OAT_FILES = 1 << 10,
DISABLE_HIDDEN_API_CHECKS = 1 << 11,
+ DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12,
};
static uint32_t EnableDebugFeatures(uint32_t runtime_flags) {
@@ -210,12 +211,6 @@ static uint32_t EnableDebugFeatures(uint32_t runtime_flags) {
runtime_flags &= ~DEBUG_ENABLE_SAFEMODE;
}
- const bool generate_debug_info = (runtime_flags & DEBUG_GENERATE_DEBUG_INFO) != 0;
- if (generate_debug_info) {
- runtime->AddCompilerOption("--generate-debug-info");
- runtime_flags &= ~DEBUG_GENERATE_DEBUG_INFO;
- }
-
// This is for backwards compatibility with Dalvik.
runtime_flags &= ~DEBUG_ENABLE_ASSERT;
@@ -229,6 +224,7 @@ static uint32_t EnableDebugFeatures(uint32_t runtime_flags) {
bool needs_non_debuggable_classes = false;
if ((runtime_flags & DEBUG_JAVA_DEBUGGABLE) != 0) {
runtime->AddCompilerOption("--debuggable");
+ runtime_flags |= DEBUG_GENERATE_MINI_DEBUG_INFO;
runtime->SetJavaDebuggable(true);
// Deoptimize the boot image as it may be non-debuggable.
runtime->DeoptimizeBootImage();
@@ -241,11 +237,23 @@ static uint32_t EnableDebugFeatures(uint32_t runtime_flags) {
if ((runtime_flags & DEBUG_NATIVE_DEBUGGABLE) != 0) {
runtime->AddCompilerOption("--debuggable");
- runtime->AddCompilerOption("--generate-debug-info");
+ runtime_flags |= DEBUG_GENERATE_DEBUG_INFO;
runtime->SetNativeDebuggable(true);
runtime_flags &= ~DEBUG_NATIVE_DEBUGGABLE;
}
+ if ((runtime_flags & DEBUG_GENERATE_MINI_DEBUG_INFO) != 0) {
+ // Generate native minimal debug information to allow backtracing.
+ runtime->AddCompilerOption("--generate-mini-debug-info");
+ runtime_flags &= ~DEBUG_GENERATE_MINI_DEBUG_INFO;
+ }
+
+ if ((runtime_flags & DEBUG_GENERATE_DEBUG_INFO) != 0) {
+ // Generate all native debug information we can (e.g. line-numbers).
+ runtime->AddCompilerOption("--generate-debug-info");
+ runtime_flags &= ~DEBUG_GENERATE_DEBUG_INFO;
+ }
+
return runtime_flags;
}
@@ -274,6 +282,7 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env,
// Our system thread ID, etc, has changed so reset Thread state.
thread->InitAfterFork();
runtime_flags = EnableDebugFeatures(runtime_flags);
+ bool do_hidden_api_checks = true;
if ((runtime_flags & DISABLE_VERIFIER) != 0) {
Runtime::Current()->DisableVerifier();
@@ -286,7 +295,7 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env,
}
if ((runtime_flags & DISABLE_HIDDEN_API_CHECKS) != 0) {
- Runtime::Current()->SetHiddenApiChecksEnabled(false);
+ do_hidden_api_checks = false;
runtime_flags &= ~DISABLE_HIDDEN_API_CHECKS;
}
@@ -337,8 +346,9 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env,
}
}
- DCHECK(!is_system_server || !Runtime::Current()->AreHiddenApiChecksEnabled())
+ DCHECK(!is_system_server || !do_hidden_api_checks)
<< "SystemServer should be forked with DISABLE_HIDDEN_API_CHECKS";
+ Runtime::Current()->SetHiddenApiChecksEnabled(do_hidden_api_checks);
if (instruction_set != nullptr && !is_system_server) {
ScopedUtfChars isa_string(env, instruction_set);
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 5544275984..2091a27ffd 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -25,6 +25,7 @@
#include "common_throws.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_file_annotations.h"
+#include "dex/utf.h"
#include "hidden_api.h"
#include "jni_internal.h"
#include "mirror/class-inl.h"
@@ -43,17 +44,12 @@
#include "reflection.h"
#include "scoped_fast_native_object_access-inl.h"
#include "scoped_thread_state_change-inl.h"
-#include "utf.h"
#include "well_known_classes.h"
namespace art {
-ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (!Runtime::Current()->AreHiddenApiChecksEnabled()) {
- return false;
- }
-
+// Returns true if the first non-ClassClass caller up the stack is in boot class path.
+static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
// Walk the stack and find the first frame not from java.lang.Class.
// This is very expensive. Save this till the last.
struct FirstNonClassClassCallerVisitor : public StackVisitor {
@@ -84,8 +80,16 @@ ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self)
FirstNonClassClassCallerVisitor visitor(self);
visitor.WalkStack();
- return visitor.caller == nullptr ||
- !visitor.caller->GetDeclaringClass()->IsBootStrapClassLoaded();
+ return visitor.caller != nullptr &&
+ visitor.caller->GetDeclaringClass()->IsBootStrapClassLoaded();
+}
+
+// Returns true if the first non-ClassClass caller up the stack is not allowed to
+// access hidden APIs. This can be *very* expensive. Never call this in a loop.
+ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return Runtime::Current()->AreHiddenApiChecksEnabled() &&
+ !IsCallerInBootClassPath(self);
}
// Returns true if the first non-ClassClass caller up the stack should not be
@@ -93,9 +97,7 @@ ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self)
template<typename T>
ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK(member != nullptr);
- return hiddenapi::IsMemberHidden(member->GetAccessFlags()) &&
- ShouldEnforceHiddenApi(self);
+ return hiddenapi::ShouldBlockAccessToMember(member, self, IsCallerInBootClassPath);
}
// Returns true if a class member should be discoverable with reflection given
@@ -109,14 +111,13 @@ ALWAYS_INLINE static bool IsDiscoverable(bool public_only,
return false;
}
- if (enforce_hidden_api && hiddenapi::IsMemberHidden(access_flags)) {
+ if (enforce_hidden_api && hiddenapi::GetMemberAction(access_flags) == hiddenapi::kDeny) {
return false;
}
return true;
}
-
ALWAYS_INLINE static inline ObjPtr<mirror::Class> DecodeClass(
const ScopedFastNativeObjectAccess& soa, jobject java_class)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -831,7 +832,6 @@ static jobject Class_newInstance(JNIEnv* env, jobject javaThis) {
return nullptr;
}
}
- hiddenapi::MaybeWarnAboutMemberAccess(constructor, soa.Self(), /* num_frames */ 1);
// Invoke the constructor.
JValue result;
uint32_t args[1] = { static_cast<uint32_t>(reinterpret_cast<uintptr_t>(receiver.Get())) };
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index db7f4bb18c..f990c0421d 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -25,7 +25,6 @@
#include "common_throws.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_file_annotations.h"
-#include "hidden_api.h"
#include "jni_internal.h"
#include "mirror/class-inl.h"
#include "mirror/field-inl.h"
@@ -162,9 +161,6 @@ static jobject Field_get(JNIEnv* env, jobject javaField, jobject javaObj) {
DCHECK(soa.Self()->IsExceptionPending());
return nullptr;
}
-
- hiddenapi::MaybeWarnAboutMemberAccess(f->GetArtField(), soa.Self(), /* num_frames */ 1);
-
// We now don't expect suspension unless an exception is thrown.
// Get the field's value, boxing if necessary.
Primitive::Type field_type = f->GetTypeAsPrimitiveType();
@@ -187,14 +183,13 @@ ALWAYS_INLINE inline static JValue GetPrimitiveField(JNIEnv* env,
DCHECK(soa.Self()->IsExceptionPending());
return JValue();
}
+
// If field is not set to be accessible, verify it can be accessed by the caller.
if (!f->IsAccessible() && !VerifyFieldAccess<false>(soa.Self(), f, o)) {
DCHECK(soa.Self()->IsExceptionPending());
return JValue();
}
- hiddenapi::MaybeWarnAboutMemberAccess(f->GetArtField(), soa.Self(), /* num_frames */ 1);
-
// We now don't expect suspension unless an exception is thrown.
// Read the value.
Primitive::Type field_type = f->GetTypeAsPrimitiveType();
@@ -356,15 +351,11 @@ static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject j
DCHECK(soa.Self()->IsExceptionPending());
return;
}
-
// If field is not set to be accessible, verify it can be accessed by the caller.
if (!f->IsAccessible() && !VerifyFieldAccess<true>(soa.Self(), f, o)) {
DCHECK(soa.Self()->IsExceptionPending());
return;
}
-
- hiddenapi::MaybeWarnAboutMemberAccess(f->GetArtField(), soa.Self(), /* num_frames */ 1);
-
SetFieldValue(o, f, field_prim_type, true, unboxed_value);
}
@@ -400,8 +391,6 @@ static void SetPrimitiveField(JNIEnv* env,
return;
}
- hiddenapi::MaybeWarnAboutMemberAccess(f->GetArtField(), soa.Self(), /* num_frames */ 1);
-
// Write the value.
SetFieldValue(o, f, field_type, false, wide_value);
}
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 307f7b96ed..dc4bae3415 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -47,6 +47,7 @@
#include "dex/dex_file_loader.h"
#include "dex/dex_file_types.h"
#include "dex/standard_dex_file.h"
+#include "dex/utf-inl.h"
#include "elf_file.h"
#include "elf_utils.h"
#include "gc_root.h"
@@ -60,7 +61,6 @@
#include "os.h"
#include "runtime.h"
#include "type_lookup_table.h"
-#include "utf-inl.h"
#include "utils.h"
#include "vdex_file.h"
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 50a706d1ba..46c692e568 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -28,12 +28,12 @@
#include "compiler_filter.h"
#include "dex/dex_file.h"
#include "dex/dex_file_layout.h"
+#include "dex/utf.h"
#include "index_bss_mapping.h"
#include "mirror/object.h"
#include "oat.h"
#include "os.h"
#include "type_lookup_table.h"
-#include "utf.h"
#include "utils.h"
namespace art {
diff --git a/runtime/primitive.h b/runtime/primitive.h
index 5b163d8cbe..38ad68d13d 100644
--- a/runtime/primitive.h
+++ b/runtime/primitive.h
@@ -140,12 +140,30 @@ class Primitive {
// Returns the descriptor corresponding to the boxed type of |type|.
static const char* BoxedDescriptor(Type type);
- // Return true if |type| is an numeric type.
+ // Returns true if |type| is an numeric type.
static constexpr bool IsNumericType(Type type) {
switch (type) {
case Primitive::Type::kPrimNot: return false;
case Primitive::Type::kPrimBoolean: return false;
case Primitive::Type::kPrimByte: return true;
+ case Primitive::Type::kPrimChar: return true;
+ case Primitive::Type::kPrimShort: return true;
+ case Primitive::Type::kPrimInt: return true;
+ case Primitive::Type::kPrimLong: return true;
+ case Primitive::Type::kPrimFloat: return true;
+ case Primitive::Type::kPrimDouble: return true;
+ case Primitive::Type::kPrimVoid: return false;
+ }
+ LOG(FATAL) << "Invalid type " << static_cast<int>(type);
+ UNREACHABLE();
+ }
+
+ // Return trues if |type| is a signed numeric type.
+ static constexpr bool IsSignedNumericType(Type type) {
+ switch (type) {
+ case Primitive::Type::kPrimNot: return false;
+ case Primitive::Type::kPrimBoolean: return false;
+ case Primitive::Type::kPrimByte: return true;
case Primitive::Type::kPrimChar: return false;
case Primitive::Type::kPrimShort: return true;
case Primitive::Type::kPrimInt: return true;
@@ -158,17 +176,39 @@ class Primitive {
UNREACHABLE();
}
+ // Returns the number of bits required to hold the largest
+ // positive number that can be represented by |type|.
+ static constexpr size_t BitsRequiredForLargestValue(Type type) {
+ switch (type) {
+ case Primitive::Type::kPrimNot: return 0u;
+ case Primitive::Type::kPrimBoolean: return 1u;
+ case Primitive::Type::kPrimByte: return 7u;
+ case Primitive::Type::kPrimChar: return 16u;
+ case Primitive::Type::kPrimShort: return 15u;
+ case Primitive::Type::kPrimInt: return 31u;
+ case Primitive::Type::kPrimLong: return 63u;
+ case Primitive::Type::kPrimFloat: return 128u;
+ case Primitive::Type::kPrimDouble: return 1024u;
+ case Primitive::Type::kPrimVoid: return 0u;
+ }
+ }
+
// Returns true if it is possible to widen type |from| to type |to|. Both |from| and
// |to| should be numeric primitive types.
static bool IsWidenable(Type from, Type to) {
- static_assert(Primitive::Type::kPrimByte < Primitive::Type::kPrimShort, "Bad ordering");
- static_assert(Primitive::Type::kPrimShort < Primitive::Type::kPrimInt, "Bad ordering");
- static_assert(Primitive::Type::kPrimInt < Primitive::Type::kPrimLong, "Bad ordering");
- static_assert(Primitive::Type::kPrimLong < Primitive::Type::kPrimFloat, "Bad ordering");
- static_assert(Primitive::Type::kPrimFloat < Primitive::Type::kPrimDouble, "Bad ordering");
- // Widening is only applicable between numeric types, like byte
- // and int. Non-numeric types, such as boolean, cannot be widened.
- return IsNumericType(from) && IsNumericType(to) && from <= to;
+ if (!IsNumericType(from) || !IsNumericType(to)) {
+ // Widening is only applicable between numeric types.
+ return false;
+ }
+ if (IsSignedNumericType(from) && !IsSignedNumericType(to)) {
+ // Nowhere to store the sign bit in |to|.
+ return false;
+ }
+ if (BitsRequiredForLargestValue(from) > BitsRequiredForLargestValue(to)) {
+ // The from,to pair corresponds to a narrowing.
+ return false;
+ }
+ return true;
}
static bool Is64BitType(Type type) {
diff --git a/runtime/primitive_test.cc b/runtime/primitive_test.cc
new file mode 100644
index 0000000000..e433b15b61
--- /dev/null
+++ b/runtime/primitive_test.cc
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+#include "primitive.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+namespace {
+
+void CheckPrimitiveTypeWidensTo(Primitive::Type from,
+ const std::vector<Primitive::Type>& expected_to_types) {
+ std::vector<Primitive::Type> actual_to_types;
+ int last = static_cast<int>(Primitive::Type::kPrimLast);
+ for (int i = 0; i <= last; ++i) {
+ Primitive::Type to = static_cast<Primitive::Type>(i);
+ if (Primitive::IsWidenable(from, to)) {
+ actual_to_types.push_back(to);
+ }
+ }
+ EXPECT_EQ(expected_to_types, actual_to_types);
+}
+
+} // namespace
+
+TEST(PrimitiveTest, NotWidensTo) {
+ const std::vector<Primitive::Type> to_types = {};
+ CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimNot, to_types);
+}
+
+TEST(PrimitiveTest, BooleanWidensTo) {
+ const std::vector<Primitive::Type> to_types = {};
+ CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimBoolean, to_types);
+}
+
+TEST(PrimitiveTest, ByteWidensTo) {
+ const std::vector<Primitive::Type> to_types = {
+ Primitive::Type::kPrimByte,
+ Primitive::Type::kPrimShort,
+ Primitive::Type::kPrimInt,
+ Primitive::Type::kPrimLong,
+ Primitive::Type::kPrimFloat,
+ Primitive::Type::kPrimDouble,
+ };
+ CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimByte, to_types);
+}
+
+TEST(PrimitiveTest, CharWidensTo) {
+ const std::vector<Primitive::Type> to_types = {
+ Primitive::Type::kPrimChar,
+ Primitive::Type::kPrimInt,
+ Primitive::Type::kPrimLong,
+ Primitive::Type::kPrimFloat,
+ Primitive::Type::kPrimDouble,
+ };
+ CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimChar, to_types);
+}
+
+TEST(PrimitiveTest, ShortWidensTo) {
+ const std::vector<Primitive::Type> to_types = {
+ Primitive::Type::kPrimShort,
+ Primitive::Type::kPrimInt,
+ Primitive::Type::kPrimLong,
+ Primitive::Type::kPrimFloat,
+ Primitive::Type::kPrimDouble,
+ };
+ CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimShort, to_types);
+}
+
+TEST(PrimitiveTest, IntWidensTo) {
+ const std::vector<Primitive::Type> to_types = {
+ Primitive::Type::kPrimInt,
+ Primitive::Type::kPrimLong,
+ Primitive::Type::kPrimFloat,
+ Primitive::Type::kPrimDouble,
+ };
+ CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimInt, to_types);
+}
+
+TEST(PrimitiveTest, LongWidensTo) {
+ const std::vector<Primitive::Type> to_types = {
+ Primitive::Type::kPrimLong,
+ Primitive::Type::kPrimFloat,
+ Primitive::Type::kPrimDouble,
+ };
+ CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimLong, to_types);
+}
+
+TEST(PrimitiveTest, FloatWidensTo) {
+ const std::vector<Primitive::Type> to_types = {
+ Primitive::Type::kPrimFloat,
+ Primitive::Type::kPrimDouble,
+ };
+ CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimFloat, to_types);
+}
+
+TEST(PrimitiveTest, DoubleWidensTo) {
+ const std::vector<Primitive::Type> to_types = {
+ Primitive::Type::kPrimDouble,
+ };
+ CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimDouble, to_types);
+}
+
+TEST(PrimitiveTest, VoidWidensTo) {
+ const std::vector<Primitive::Type> to_types = {};
+ CheckPrimitiveTypeWidensTo(Primitive::Type::kPrimVoid, to_types);
+}
+
+} // namespace art
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 3a7640fa8b..006405f095 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -222,7 +222,7 @@ void QuickExceptionHandler::SetCatchEnvironmentForOptimizedHandler(StackVisitor*
self_->DumpStack(LOG_STREAM(INFO) << "Setting catch phis: ");
}
- CodeItemDataAccessor accessor(handler_method_);
+ CodeItemDataAccessor accessor(handler_method_->DexInstructionData());
const size_t number_of_vregs = accessor.RegistersSize();
CodeInfo code_info = handler_method_header_->GetOptimizedCodeInfo();
CodeInfoEncoding encoding = code_info.ExtractEncoding();
@@ -360,7 +360,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor {
const size_t frame_id = GetFrameId();
ShadowFrame* new_frame = GetThread()->FindDebuggerShadowFrame(frame_id);
const bool* updated_vregs;
- CodeItemDataAccessor accessor(method);
+ CodeItemDataAccessor accessor(method->DexInstructionData());
const size_t num_regs = accessor.RegistersSize();
if (new_frame == nullptr) {
new_frame = ShadowFrame::CreateDeoptimizedFrame(num_regs, nullptr, method, GetDexPc());
@@ -408,7 +408,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor {
uintptr_t native_pc_offset = method_header->NativeQuickPcOffset(GetCurrentQuickFramePc());
CodeInfoEncoding encoding = code_info.ExtractEncoding();
StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
- CodeItemDataAccessor accessor(m);
+ CodeItemDataAccessor accessor(m->DexInstructionData());
const size_t number_of_vregs = accessor.RegistersSize();
uint32_t register_mask = code_info.GetRegisterMaskOf(encoding, stack_map);
BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, stack_map);
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 6ffafe02f1..635a03afe0 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -465,9 +465,6 @@ JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, jobject o
}
ArtMethod* method = jni::DecodeArtMethod(mid);
-
- hiddenapi::MaybeWarnAboutMemberAccess(method, soa.Self(), /* num_frames */ 1);
-
bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
if (is_string_init) {
// Replace calls to String.<init> with equivalent StringFactory call.
@@ -499,9 +496,6 @@ JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, jobject o
}
ArtMethod* method = jni::DecodeArtMethod(mid);
-
- hiddenapi::MaybeWarnAboutMemberAccess(method, soa.Self(), /* num_frames */ 1);
-
bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
if (is_string_init) {
// Replace calls to String.<init> with equivalent StringFactory call.
@@ -534,9 +528,6 @@ JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnab
ObjPtr<mirror::Object> receiver = soa.Decode<mirror::Object>(obj);
ArtMethod* method = FindVirtualMethod(receiver, jni::DecodeArtMethod(mid));
-
- hiddenapi::MaybeWarnAboutMemberAccess(method, soa.Self(), /* num_frames */ 1);
-
bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
if (is_string_init) {
// Replace calls to String.<init> with equivalent StringFactory call.
@@ -569,9 +560,6 @@ JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnab
ObjPtr<mirror::Object> receiver = soa.Decode<mirror::Object>(obj);
ArtMethod* method = FindVirtualMethod(receiver, jni::DecodeArtMethod(mid));
-
- hiddenapi::MaybeWarnAboutMemberAccess(method, soa.Self(), /* num_frames */ 1);
-
bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
if (is_string_init) {
// Replace calls to String.<init> with equivalent StringFactory call.
@@ -616,8 +604,6 @@ jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaM
}
}
- hiddenapi::MaybeWarnAboutMemberAccess(m, soa.Self(), num_frames);
-
ObjPtr<mirror::Object> receiver;
if (!m->IsStatic()) {
// Replace calls to String.<init> with equivalent StringFactory call.
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 6d065d6146..5a3a6f0cf4 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -265,7 +265,7 @@ Runtime::Runtime()
oat_file_manager_(nullptr),
is_low_memory_mode_(false),
safe_mode_(false),
- do_hidden_api_checks_(false),
+ do_hidden_api_checks_(true),
pending_hidden_api_warning_(false),
dedupe_hidden_api_warnings_(true),
dump_native_stack_on_sig_quit_(true),
@@ -1171,7 +1171,9 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
target_sdk_version_ = runtime_options.GetOrDefault(Opt::TargetSdkVersion);
- if (runtime_options.Exists(Opt::NoHiddenApiChecks)) {
+ // Check whether to enforce hidden API access checks. Zygote needs to be exempt
+ // but checks may be enabled for forked processes (see dalvik_system_ZygoteHooks).
+ if (is_zygote_ || runtime_options.Exists(Opt::NoHiddenApiChecks)) {
do_hidden_api_checks_ = false;
}
@@ -1253,7 +1255,20 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
jdwp_provider_ = runtime_options.GetOrDefault(Opt::JdwpProvider);
switch (jdwp_provider_) {
case JdwpProvider::kNone: {
- LOG(WARNING) << "Disabling all JDWP support.";
+ LOG(INFO) << "Disabling all JDWP support.";
+ if (!jdwp_options_.empty()) {
+ bool has_transport = jdwp_options_.find("transport") != std::string::npos;
+ const char* transport_internal = !has_transport ? "transport=dt_android_adb," : "";
+ std::string adb_connection_args =
+ std::string(" -XjdwpProvider:adbconnection -XjdwpOptions:") + jdwp_options_;
+ LOG(WARNING) << "Jdwp options given when jdwp is disabled! You probably want to enable "
+ << "jdwp with one of:" << std::endl
+ << " -XjdwpProvider:internal "
+ << "-XjdwpOptions:" << transport_internal << jdwp_options_ << std::endl
+ << " -Xplugin:libopenjdkjvmti" << (kIsDebugBuild ? "d" : "") << ".so "
+ << "-agentpath:libjdwp.so=" << jdwp_options_ << std::endl
+ << (has_transport ? "" : adb_connection_args);
+ }
break;
}
case JdwpProvider::kInternal: {
@@ -1855,7 +1870,13 @@ void Runtime::BlockSignals() {
bool Runtime::AttachCurrentThread(const char* thread_name, bool as_daemon, jobject thread_group,
bool create_peer) {
ScopedTrace trace(__FUNCTION__);
- return Thread::Attach(thread_name, as_daemon, thread_group, create_peer) != nullptr;
+ Thread* self = Thread::Attach(thread_name, as_daemon, thread_group, create_peer);
+ // Run ThreadGroup.add to notify the group that this thread is now started.
+ if (self != nullptr && create_peer && !IsAotCompiler()) {
+ ScopedObjectAccess soa(self);
+ self->NotifyThreadGroup(soa, thread_group);
+ }
+ return self != nullptr;
}
void Runtime::DetachCurrentThread() {
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 6e1a68b07d..e78d952c1c 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -44,7 +44,7 @@ RUNTIME_OPTIONS_KEY (std::string, Image)
RUNTIME_OPTIONS_KEY (Unit, CheckJni)
RUNTIME_OPTIONS_KEY (Unit, JniOptsForceCopy)
RUNTIME_OPTIONS_KEY (std::string, JdwpOptions, "")
-RUNTIME_OPTIONS_KEY (JdwpProvider, JdwpProvider, JdwpProvider::kInternal)
+RUNTIME_OPTIONS_KEY (JdwpProvider, JdwpProvider, JdwpProvider::kNone)
RUNTIME_OPTIONS_KEY (MemoryKiB, MemoryMaximumSize, gc::Heap::kDefaultMaximumSize) // -Xmx
RUNTIME_OPTIONS_KEY (MemoryKiB, MemoryInitialSize, gc::Heap::kDefaultInitialSize) // -Xms
RUNTIME_OPTIONS_KEY (MemoryKiB, HeapGrowthLimit) // Default is 0 for unlimited
diff --git a/runtime/stack.cc b/runtime/stack.cc
index dfdea28ae8..229238e0f7 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -154,7 +154,7 @@ mirror::Object* StackVisitor::GetThisObject() const {
return cur_shadow_frame_->GetVRegReference(0);
}
} else {
- CodeItemDataAccessor accessor(m);
+ CodeItemDataAccessor accessor(m->DexInstructionData());
if (!accessor.HasCodeItem()) {
UNIMPLEMENTED(ERROR) << "Failed to determine this object of abstract or proxy method: "
<< ArtMethod::PrettyMethod(m);
@@ -225,7 +225,7 @@ bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKin
DCHECK_EQ(m, GetMethod());
// Can't be null or how would we compile its instructions?
DCHECK(m->GetCodeItem() != nullptr) << m->PrettyMethod();
- CodeItemDataAccessor accessor(m);
+ CodeItemDataAccessor accessor(m->DexInstructionData());
uint16_t number_of_dex_registers = accessor.RegistersSize();
DCHECK_LT(vreg, number_of_dex_registers);
const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
@@ -395,7 +395,7 @@ bool StackVisitor::SetVReg(ArtMethod* m,
uint16_t vreg,
uint32_t new_value,
VRegKind kind) {
- CodeItemDataAccessor accessor(m);
+ CodeItemDataAccessor accessor(m->DexInstructionData());
if (!accessor.HasCodeItem()) {
return false;
}
@@ -432,7 +432,7 @@ bool StackVisitor::SetVRegPair(ArtMethod* m,
LOG(FATAL) << "Expected long or double: kind_lo=" << kind_lo << ", kind_hi=" << kind_hi;
UNREACHABLE();
}
- CodeItemDataAccessor accessor(m);
+ CodeItemDataAccessor accessor(m->DexInstructionData());
if (!accessor.HasCodeItem()) {
return false;
}
diff --git a/runtime/string_reference.h b/runtime/string_reference.h
index 97661c6019..1ee5d6d53a 100644
--- a/runtime/string_reference.h
+++ b/runtime/string_reference.h
@@ -24,7 +24,7 @@
#include "dex/dex_file-inl.h"
#include "dex/dex_file_reference.h"
#include "dex/dex_file_types.h"
-#include "utf-inl.h"
+#include "dex/utf-inl.h"
namespace art {
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 46cb751b93..9dc92f3788 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2049,20 +2049,8 @@ void Thread::FinishStartup() {
// The thread counts as started from now on. We need to add it to the ThreadGroup. For regular
// threads, this is done in Thread.start() on the Java side.
- {
- // This is only ever done once. There's no benefit in caching the method.
- jmethodID thread_group_add = soa.Env()->GetMethodID(WellKnownClasses::java_lang_ThreadGroup,
- "add",
- "(Ljava/lang/Thread;)V");
- CHECK(thread_group_add != nullptr);
- ScopedLocalRef<jobject> thread_jobject(
- soa.Env(), soa.Env()->AddLocalReference<jobject>(Thread::Current()->GetPeer()));
- soa.Env()->CallNonvirtualVoidMethod(runtime->GetMainThreadGroup(),
- WellKnownClasses::java_lang_ThreadGroup,
- thread_group_add,
- thread_jobject.get());
- Thread::Current()->AssertNoPendingException();
- }
+ Thread::Current()->NotifyThreadGroup(soa, runtime->GetMainThreadGroup());
+ Thread::Current()->AssertNoPendingException();
}
void Thread::Shutdown() {
@@ -2076,6 +2064,28 @@ void Thread::Shutdown() {
}
}
+void Thread::NotifyThreadGroup(ScopedObjectAccessAlreadyRunnable& soa, jobject thread_group) {
+ ScopedLocalRef<jobject> thread_jobject(
+ soa.Env(), soa.Env()->AddLocalReference<jobject>(Thread::Current()->GetPeer()));
+ ScopedLocalRef<jobject> thread_group_jobject_scoped(
+ soa.Env(), nullptr);
+ jobject thread_group_jobject = thread_group;
+ if (thread_group == nullptr || kIsDebugBuild) {
+ // There is always a group set. Retrieve it.
+ thread_group_jobject_scoped.reset(
+ soa.Env()->GetObjectField(thread_jobject.get(),
+ WellKnownClasses::java_lang_Thread_group));
+ thread_group_jobject = thread_group_jobject_scoped.get();
+ if (kIsDebugBuild && thread_group != nullptr) {
+ CHECK(soa.Env()->IsSameObject(thread_group, thread_group_jobject));
+ }
+ }
+ soa.Env()->CallNonvirtualVoidMethod(thread_group_jobject,
+ WellKnownClasses::java_lang_ThreadGroup,
+ WellKnownClasses::java_lang_ThreadGroup_add,
+ thread_jobject.get());
+}
+
Thread::Thread(bool daemon)
: tls32_(daemon),
wait_monitor_(nullptr),
@@ -3618,7 +3628,7 @@ class ReferenceMapVisitor : public StackVisitor {
const CodeInfoEncoding& _encoding,
const StackMap& map,
RootVisitor& _visitor)
- : number_of_dex_registers(CodeItemDataAccessor(method).RegistersSize()),
+ : number_of_dex_registers(method->DexInstructionData().RegistersSize()),
code_info(_code_info),
encoding(_encoding),
dex_register_map(code_info.GetDexRegisterMapOf(map,
diff --git a/runtime/thread.h b/runtime/thread.h
index 426d27d1b4..295685e799 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -511,6 +511,12 @@ class Thread {
static void FinishStartup();
static void Shutdown();
+ // Notify this thread's thread-group that this thread has started.
+ // Note: the given thread-group is used as a fast path and verified in debug build. If the value
+ // is null, the thread's thread-group is loaded from the peer.
+ void NotifyThreadGroup(ScopedObjectAccessAlreadyRunnable& soa, jobject thread_group = nullptr)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// JNI methods
JNIEnvExt* GetJniEnv() const {
return tlsPtr_.jni_env;
diff --git a/runtime/type_lookup_table.cc b/runtime/type_lookup_table.cc
index 649a4f9547..925a9089cb 100644
--- a/runtime/type_lookup_table.cc
+++ b/runtime/type_lookup_table.cc
@@ -21,7 +21,7 @@
#include "base/bit_utils.h"
#include "dex/dex_file-inl.h"
-#include "utf-inl.h"
+#include "dex/utf-inl.h"
#include "utils.h"
namespace art {
diff --git a/runtime/type_lookup_table.h b/runtime/type_lookup_table.h
index 50c93ad9f0..a1f9519f18 100644
--- a/runtime/type_lookup_table.h
+++ b/runtime/type_lookup_table.h
@@ -18,8 +18,8 @@
#define ART_RUNTIME_TYPE_LOOKUP_TABLE_H_
#include "dex/dex_file_types.h"
+#include "dex/utf.h"
#include "leb128.h"
-#include "utf.h"
namespace art {
diff --git a/runtime/type_lookup_table_test.cc b/runtime/type_lookup_table_test.cc
index d04652a8e7..b6ab6da78c 100644
--- a/runtime/type_lookup_table_test.cc
+++ b/runtime/type_lookup_table_test.cc
@@ -20,8 +20,8 @@
#include "common_runtime_test.h"
#include "dex/dex_file-inl.h"
+#include "dex/utf-inl.h"
#include "scoped_thread_state_change-inl.h"
-#include "utf-inl.h"
namespace art {
diff --git a/runtime/utils.cc b/runtime/utils.cc
index b2ec669f32..393b18e1b3 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -30,8 +30,8 @@
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
+#include "dex/utf-inl.h"
#include "os.h"
-#include "utf-inl.h"
#if defined(__APPLE__)
#include <crt_externs.h>
@@ -151,57 +151,6 @@ std::string PrettySize(int64_t byte_count) {
negative_str, byte_count / kBytesPerUnit[i], kUnitStrings[i]);
}
-static inline constexpr bool NeedsEscaping(uint16_t ch) {
- return (ch < ' ' || ch > '~');
-}
-
-std::string PrintableChar(uint16_t ch) {
- std::string result;
- result += '\'';
- if (NeedsEscaping(ch)) {
- StringAppendF(&result, "\\u%04x", ch);
- } else {
- result += static_cast<std::string::value_type>(ch);
- }
- result += '\'';
- return result;
-}
-
-std::string PrintableString(const char* utf) {
- std::string result;
- result += '"';
- const char* p = utf;
- size_t char_count = CountModifiedUtf8Chars(p);
- for (size_t i = 0; i < char_count; ++i) {
- uint32_t ch = GetUtf16FromUtf8(&p);
- if (ch == '\\') {
- result += "\\\\";
- } else if (ch == '\n') {
- result += "\\n";
- } else if (ch == '\r') {
- result += "\\r";
- } else if (ch == '\t') {
- result += "\\t";
- } else {
- const uint16_t leading = GetLeadingUtf16Char(ch);
-
- if (NeedsEscaping(leading)) {
- StringAppendF(&result, "\\u%04x", leading);
- } else {
- result += static_cast<std::string::value_type>(leading);
- }
-
- const uint32_t trailing = GetTrailingUtf16Char(ch);
- if (trailing != 0) {
- // All high surrogates will need escaping.
- StringAppendF(&result, "\\u%04x", trailing);
- }
- }
- }
- result += '"';
- return result;
-}
-
std::string GetJniShortName(const std::string& class_descriptor, const std::string& method) {
// Remove the leading 'L' and trailing ';'...
std::string class_name(class_descriptor);
diff --git a/runtime/utils.h b/runtime/utils.h
index abdafcc9f7..443b0cc398 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -67,12 +67,6 @@ static inline uint32_t PointerToLowMemUInt32(const void* p) {
return intp & 0xFFFFFFFFU;
}
-std::string PrintableChar(uint16_t ch);
-
-// Returns an ASCII string corresponding to the given UTF-8 string.
-// Java escapes are used for non-ASCII characters.
-std::string PrintableString(const char* utf8);
-
// Used to implement PrettyClass, PrettyField, PrettyMethod, and PrettyTypeOf,
// one of which is probably more useful to you.
// Returns a human-readable equivalent of 'descriptor'. So "I" would be "int",
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index 36ebb17f4f..7428e98dbb 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -30,6 +30,8 @@
#include "dex/dex_file.h"
#include "dex/dex_file_loader.h"
#include "dex_to_dex_decompiler.h"
+#include "hidden_api_access_flags.h"
+#include "leb128.h"
#include "quicken_info.h"
namespace art {
@@ -262,6 +264,18 @@ void VdexFile::UnquickenDexFile(const DexFile& target_dex_file,
UnquickenDexFile(target_dex_file, source_dex_file.Begin(), decompile_return_instruction);
}
+static void UpdateAccessFlags(uint8_t* data, uint32_t new_flag, bool is_method) {
+ // Go back 1 uleb to start.
+ data = ReverseSearchUnsignedLeb128(data);
+ if (is_method) {
+ // Methods have another uleb field before the access flags
+ data = ReverseSearchUnsignedLeb128(data);
+ }
+ DCHECK_EQ(HiddenApiAccessFlags::RemoveFromDex(DecodeUnsignedLeb128WithoutMovingCursor(data)),
+ new_flag);
+ UpdateUnsignedLeb128(data, new_flag);
+}
+
void VdexFile::UnquickenDexFile(const DexFile& target_dex_file,
const uint8_t* source_dex_begin,
bool decompile_return_instruction) const {
@@ -280,27 +294,32 @@ void VdexFile::UnquickenDexFile(const DexFile& target_dex_file,
for (ClassDataItemIterator class_it(target_dex_file, class_data);
class_it.HasNext();
class_it.Next()) {
- if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) {
+ if (class_it.IsAtMethod()) {
const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem();
- if (!unquickened_code_item.emplace(code_item).second) {
- // Already unquickened this code item, do not do it again.
- continue;
- }
- ArrayRef<const uint8_t> quicken_data;
- if (!quickening_info.empty()) {
- const uint32_t quickening_offset = GetQuickeningInfoOffset(
- GetQuickenInfoOffsetTable(source_dex_begin,
- target_dex_file.NumMethodIds(),
- quickening_info),
- class_it.GetMemberIndex(),
- quickening_info);
- quicken_data = GetQuickeningInfoAt(quickening_info, quickening_offset);
+ if (code_item != nullptr && unquickened_code_item.emplace(code_item).second) {
+ ArrayRef<const uint8_t> quicken_data;
+ if (!quickening_info.empty()) {
+ const uint32_t quickening_offset = GetQuickeningInfoOffset(
+ GetQuickenInfoOffsetTable(source_dex_begin,
+ target_dex_file.NumMethodIds(),
+ quickening_info),
+ class_it.GetMemberIndex(),
+ quickening_info);
+ quicken_data = GetQuickeningInfoAt(quickening_info, quickening_offset);
+ }
+ optimizer::ArtDecompileDEX(
+ target_dex_file,
+ *code_item,
+ quicken_data,
+ decompile_return_instruction);
}
- optimizer::ArtDecompileDEX(
- target_dex_file,
- *code_item,
- quicken_data,
- decompile_return_instruction);
+ UpdateAccessFlags(const_cast<uint8_t*>(class_it.DataPointer()),
+ class_it.GetMemberAccessFlags(),
+ /*is_method*/ true);
+ } else {
+ UpdateAccessFlags(const_cast<uint8_t*>(class_it.DataPointer()),
+ class_it.GetMemberAccessFlags(),
+ /*is_method*/ false);
}
}
}
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index b9fd467017..0f347952c9 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -87,8 +87,8 @@ class VdexFile {
private:
static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' };
- // Last update: Separate section for compact dex data.
- static constexpr uint8_t kVdexVersion[] = { '0', '1', '6', '\0' };
+ // Last update: Fix separate section for compact dex data.
+ static constexpr uint8_t kVdexVersion[] = { '0', '1', '7', '\0' };
uint8_t magic_[4];
uint8_t version_[4];
@@ -101,6 +101,9 @@ class VdexFile {
friend class VdexFile;
};
+ // Note: The file is called "primary" to match the naming with profiles.
+ static const constexpr char* kVdexNameInDmFile = "primary.vdex";
+
typedef uint32_t VdexChecksum;
using QuickeningTableOffsetType = uint32_t;
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index dc57f81a67..5fe10f5c12 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -110,6 +110,7 @@ jmethodID WellKnownClasses::java_lang_System_runFinalization = nullptr;
jmethodID WellKnownClasses::java_lang_Thread_dispatchUncaughtException;
jmethodID WellKnownClasses::java_lang_Thread_init;
jmethodID WellKnownClasses::java_lang_Thread_run;
+jmethodID WellKnownClasses::java_lang_ThreadGroup_add;
jmethodID WellKnownClasses::java_lang_ThreadGroup_removeThread;
jmethodID WellKnownClasses::java_nio_DirectByteBuffer_init;
jmethodID WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation;
@@ -347,6 +348,7 @@ void WellKnownClasses::Init(JNIEnv* env) {
java_lang_Thread_dispatchUncaughtException = CacheMethod(env, java_lang_Thread, false, "dispatchUncaughtException", "(Ljava/lang/Throwable;)V");
java_lang_Thread_init = CacheMethod(env, java_lang_Thread, false, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
java_lang_Thread_run = CacheMethod(env, java_lang_Thread, false, "run", "()V");
+ java_lang_ThreadGroup_add = CacheMethod(env, java_lang_ThreadGroup, false, "add", "(Ljava/lang/Thread;)V");
java_lang_ThreadGroup_removeThread = CacheMethod(env, java_lang_ThreadGroup, false, "threadTerminated", "(Ljava/lang/Thread;)V");
java_nio_DirectByteBuffer_init = CacheMethod(env, java_nio_DirectByteBuffer, false, "<init>", "(JI)V");
libcore_reflect_AnnotationFactory_createAnnotation = CacheMethod(env, libcore_reflect_AnnotationFactory, true, "createAnnotation", "(Ljava/lang/Class;[Llibcore/reflect/AnnotationMember;)Ljava/lang/annotation/Annotation;");
@@ -496,6 +498,7 @@ void WellKnownClasses::Clear() {
java_lang_Thread_dispatchUncaughtException = nullptr;
java_lang_Thread_init = nullptr;
java_lang_Thread_run = nullptr;
+ java_lang_ThreadGroup_add = nullptr;
java_lang_ThreadGroup_removeThread = nullptr;
java_nio_DirectByteBuffer_init = nullptr;
libcore_reflect_AnnotationFactory_createAnnotation = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 024971ae3d..9e0b079b7b 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -121,6 +121,7 @@ struct WellKnownClasses {
static jmethodID java_lang_Thread_dispatchUncaughtException;
static jmethodID java_lang_Thread_init;
static jmethodID java_lang_Thread_run;
+ static jmethodID java_lang_ThreadGroup_add;
static jmethodID java_lang_ThreadGroup_removeThread;
static jmethodID java_nio_DirectByteBuffer_init;
static jmethodID libcore_reflect_AnnotationFactory_createAnnotation;
diff --git a/test/004-ThreadStress/src/Main.java b/test/004-ThreadStress/src-art/Main.java
index 6ad160c1a6..a142934638 100644
--- a/test/004-ThreadStress/src/Main.java
+++ b/test/004-ThreadStress/src-art/Main.java
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+import dalvik.system.VMRuntime;
+
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
@@ -32,23 +34,26 @@ import java.util.concurrent.Semaphore;
// (It is important to pass Main if you want to give parameters...)
//
// ThreadStress command line parameters:
-// -n X ............ number of threads
-// -d X ............ number of daemon threads
-// -o X ............ number of overall operations
-// -t X ............ number of operations per thread
-// -p X ............ number of permits granted by semaphore
-// --dumpmap ....... print the frequency map
-// -oom:X .......... frequency of OOM (double)
-// -sigquit:X ...... frequency of SigQuit (double)
-// -alloc:X ........ frequency of Alloc (double)
-// -largealloc:X ... frequency of LargeAlloc (double)
-// -stacktrace:X ... frequency of StackTrace (double)
-// -exit:X ......... frequency of Exit (double)
-// -sleep:X ........ frequency of Sleep (double)
-// -wait:X ......... frequency of Wait (double)
-// -timedwait:X .... frequency of TimedWait (double)
-// -syncandwork:X .. frequency of SyncAndWork (double)
-// -queuedwait:X ... frequency of QueuedWait (double)
+// -n X .............. number of threads
+// -d X .............. number of daemon threads
+// -o X .............. number of overall operations
+// -t X .............. number of operations per thread
+// -p X .............. number of permits granted by semaphore
+// --dumpmap ......... print the frequency map
+// --locks-only ...... select a pre-set frequency map with lock-related operations only
+// --allocs-only ..... select a pre-set frequency map with allocation-related operations only
+// -oom:X ............ frequency of OOM (double)
+// -sigquit:X ........ frequency of SigQuit (double)
+// -alloc:X .......... frequency of Alloc (double)
+// -largealloc:X ..... frequency of LargeAlloc (double)
+// -nonmovingalloc:X.. frequency of NonMovingAlloc (double)
+// -stacktrace:X ..... frequency of StackTrace (double)
+// -exit:X ........... frequency of Exit (double)
+// -sleep:X .......... frequency of Sleep (double)
+// -wait:X ........... frequency of Wait (double)
+// -timedwait:X ...... frequency of TimedWait (double)
+// -syncandwork:X .... frequency of SyncAndWork (double)
+// -queuedwait:X ..... frequency of QueuedWait (double)
public class Main implements Runnable {
@@ -156,6 +161,25 @@ public class Main implements Runnable {
}
}
+ private final static class NonMovingAlloc extends Operation {
+ private final static int ALLOC_SIZE = 1024; // Needs to be small enough to not be in LOS.
+ private final static int ALLOC_COUNT = 1024;
+ private final static VMRuntime runtime = VMRuntime.getRuntime();
+
+ @Override
+ public boolean perform() {
+ try {
+ List<byte[]> l = new ArrayList<byte[]>();
+ for (int i = 0; i < ALLOC_COUNT; i++) {
+ l.add((byte[]) runtime.newNonMovableArray(byte.class, ALLOC_SIZE));
+ }
+ } catch (OutOfMemoryError e) {
+ }
+ return true;
+ }
+ }
+
+
private final static class StackTrace extends Operation {
@Override
public boolean perform() {
@@ -289,26 +313,39 @@ public class Main implements Runnable {
private final static Map<Operation, Double> createDefaultFrequencyMap(Object lock,
Semaphore semaphore) {
Map<Operation, Double> frequencyMap = new HashMap<Operation, Double>();
- frequencyMap.put(new OOM(), 0.005); // 1/200
- frequencyMap.put(new SigQuit(), 0.095); // 19/200
- frequencyMap.put(new Alloc(), 0.225); // 45/200
- frequencyMap.put(new LargeAlloc(), 0.05); // 10/200
- frequencyMap.put(new StackTrace(), 0.1); // 20/200
- frequencyMap.put(new Exit(), 0.225); // 45/200
- frequencyMap.put(new Sleep(), 0.125); // 25/200
- frequencyMap.put(new TimedWait(lock), 0.05); // 10/200
- frequencyMap.put(new Wait(lock), 0.075); // 15/200
- frequencyMap.put(new QueuedWait(semaphore), 0.05); // 10/200
+ frequencyMap.put(new OOM(), 0.005); // 1/200
+ frequencyMap.put(new SigQuit(), 0.095); // 19/200
+ frequencyMap.put(new Alloc(), 0.225); // 45/200
+ frequencyMap.put(new LargeAlloc(), 0.05); // 10/200
+ // TODO: NonMovingAlloc operations fail an assertion with the
+ // GSS collector (see b/72738921); disable them for now.
+ frequencyMap.put(new NonMovingAlloc(), 0.0); // 0/200
+ frequencyMap.put(new StackTrace(), 0.1); // 20/200
+ frequencyMap.put(new Exit(), 0.225); // 45/200
+ frequencyMap.put(new Sleep(), 0.125); // 25/200
+ frequencyMap.put(new TimedWait(lock), 0.05); // 10/200
+ frequencyMap.put(new Wait(lock), 0.075); // 15/200
+ frequencyMap.put(new QueuedWait(semaphore), 0.05); // 10/200
+
+ return frequencyMap;
+ }
+
+ private final static Map<Operation, Double> createAllocFrequencyMap() {
+ Map<Operation, Double> frequencyMap = new HashMap<Operation, Double>();
+ frequencyMap.put(new Sleep(), 0.2); // 40/200
+ frequencyMap.put(new Alloc(), 0.575); // 115/200
+ frequencyMap.put(new LargeAlloc(), 0.15); // 30/200
+ frequencyMap.put(new NonMovingAlloc(), 0.075); // 15/200
return frequencyMap;
}
private final static Map<Operation, Double> createLockFrequencyMap(Object lock) {
Map<Operation, Double> frequencyMap = new HashMap<Operation, Double>();
- frequencyMap.put(new Sleep(), 0.2); // 40/200
- frequencyMap.put(new TimedWait(lock), 0.2); // 40/200
- frequencyMap.put(new Wait(lock), 0.2); // 40/200
- frequencyMap.put(new SyncAndWork(lock), 0.4); // 80/200
+ frequencyMap.put(new Sleep(), 0.2); // 40/200
+ frequencyMap.put(new TimedWait(lock), 0.2); // 40/200
+ frequencyMap.put(new Wait(lock), 0.2); // 40/200
+ frequencyMap.put(new SyncAndWork(lock), 0.4); // 80/200
return frequencyMap;
}
@@ -414,11 +451,14 @@ public class Main implements Runnable {
i++;
permits = Integer.parseInt(args[i]);
} else if (args[i].equals("--locks-only")) {
- lock = new Object();
frequencyMap = createLockFrequencyMap(lock);
+ } else if (args[i].equals("--allocs-only")) {
+ frequencyMap = createAllocFrequencyMap();
} else if (args[i].equals("--dumpmap")) {
dumpMap = true;
} else {
+ // Processing an argument of the form "-<operation>:X"
+ // (where X is a double value).
Semaphore semaphore = getSemaphore(permits);
frequencyMap = updateFrequencyMap(frequencyMap, lock, semaphore, args[i]);
}
diff --git a/test/044-proxy/src/Main.java b/test/044-proxy/src/Main.java
index e44c122e3d..7b70e65b8c 100644
--- a/test/044-proxy/src/Main.java
+++ b/test/044-proxy/src/Main.java
@@ -54,4 +54,8 @@ public class Main {
private static final HashMap<String, String> proxyClassNameMap = new HashMap<String, String>();
private static int uniqueTestProxyClassNum = 0;
+
+ static native void startJit();
+ static native void stopJit();
+ static native void waitForCompilation();
}
diff --git a/test/044-proxy/src/OOMEOnDispatch.java b/test/044-proxy/src/OOMEOnDispatch.java
index 94f267980d..2ee57926ae 100644
--- a/test/044-proxy/src/OOMEOnDispatch.java
+++ b/test/044-proxy/src/OOMEOnDispatch.java
@@ -32,6 +32,11 @@ public class OOMEOnDispatch implements InvocationHandler {
OOMEInterface.class.getClassLoader(), new Class[] { OOMEInterface.class },
handler);
+ // Stop the JIT to be sure nothing is running that could be resolving classes or causing
+ // verification.
+ Main.stopJit();
+ Main.waitForCompilation();
+
int l = 1024 * 1024;
while (l > 8) {
try {
@@ -40,17 +45,6 @@ public class OOMEOnDispatch implements InvocationHandler {
l = l/2;
}
}
- // Have an extra run with the exact size of Method objects. The above loop should have
- // filled with enough large objects for simplicity and speed, but ensure exact allocation
- // size.
- final int methodAsByteArrayLength = 40 - 12; // Method size - byte array overhead.
- for (;;) {
- try {
- storage.add(new byte[methodAsByteArrayLength]);
- } catch (OutOfMemoryError e) {
- break;
- }
- }
try {
inf.foo();
@@ -60,6 +54,8 @@ public class OOMEOnDispatch implements InvocationHandler {
storage.clear();
System.out.println("Received OOME");
}
+
+ Main.startJit();
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
diff --git a/test/141-class-unload/jni_unload.cc b/test/141-class-unload/jni_unload.cc
index 355457d68d..894ae8b0d7 100644
--- a/test/141-class-unload/jni_unload.cc
+++ b/test/141-class-unload/jni_unload.cc
@@ -32,19 +32,5 @@ extern "C" JNIEXPORT void JNICALL Java_IntHolder_waitForCompilation(JNIEnv*, jcl
}
}
-extern "C" JNIEXPORT void JNICALL Java_Main_stopJit(JNIEnv*, jclass) {
- jit::Jit* jit = Runtime::Current()->GetJit();
- if (jit != nullptr) {
- jit->Stop();
- }
-}
-
-extern "C" JNIEXPORT void JNICALL Java_Main_startJit(JNIEnv*, jclass) {
- jit::Jit* jit = Runtime::Current()->GetJit();
- if (jit != nullptr) {
- jit->Start();
- }
-}
-
} // namespace
} // namespace art
diff --git a/test/169-threadgroup-jni/expected.txt b/test/169-threadgroup-jni/expected.txt
new file mode 100644
index 0000000000..6a5618ebc6
--- /dev/null
+++ b/test/169-threadgroup-jni/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/169-threadgroup-jni/info.txt b/test/169-threadgroup-jni/info.txt
new file mode 100644
index 0000000000..b4c77e232b
--- /dev/null
+++ b/test/169-threadgroup-jni/info.txt
@@ -0,0 +1 @@
+Ensure that attached threads are correctly handled in ThreadGroups.
diff --git a/test/169-threadgroup-jni/jni_daemon_thread.cc b/test/169-threadgroup-jni/jni_daemon_thread.cc
new file mode 100644
index 0000000000..94902dcf2c
--- /dev/null
+++ b/test/169-threadgroup-jni/jni_daemon_thread.cc
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#include <jni.h>
+#include <nativehelper/scoped_local_ref.h>
+#include <pthread.h>
+
+#include <android-base/logging.h>
+
+namespace art {
+
+static JavaVM* vm = nullptr;
+
+static void* Runner(void* arg) {
+ CHECK(vm != nullptr);
+
+ jobject thread_group = reinterpret_cast<jobject>(arg);
+ JNIEnv* env = nullptr;
+ JavaVMAttachArgs args = { JNI_VERSION_1_6, __FUNCTION__, thread_group };
+ int attach_result = vm->AttachCurrentThread(&env, &args);
+ CHECK_EQ(attach_result, 0);
+
+ {
+ ScopedLocalRef<jclass> klass(env, env->FindClass("Main"));
+ CHECK(klass != nullptr);
+
+ jmethodID id = env->GetStaticMethodID(klass.get(), "runFromNative", "()V");
+ CHECK(id != nullptr);
+
+ env->CallStaticVoidMethod(klass.get(), id);
+ }
+
+ int detach_result = vm->DetachCurrentThread();
+ CHECK_EQ(detach_result, 0);
+ return nullptr;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_testNativeThread(
+ JNIEnv* env, jclass, jobject thread_group) {
+ CHECK_EQ(env->GetJavaVM(&vm), 0);
+ jobject global_thread_group = env->NewGlobalRef(thread_group);
+
+ pthread_t pthread;
+ int pthread_create_result = pthread_create(&pthread, nullptr, Runner, global_thread_group);
+ CHECK_EQ(pthread_create_result, 0);
+ int pthread_join_result = pthread_join(pthread, nullptr);
+ CHECK_EQ(pthread_join_result, 0);
+
+ env->DeleteGlobalRef(global_thread_group);
+}
+
+} // namespace art
diff --git a/test/169-threadgroup-jni/src/Main.java b/test/169-threadgroup-jni/src/Main.java
new file mode 100644
index 0000000000..2cd1fcfa24
--- /dev/null
+++ b/test/169-threadgroup-jni/src/Main.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+
+ ThreadGroup group = new ThreadGroup("Test group");
+ group.setDaemon(true);
+
+ testNativeThread(group);
+
+ if (!executed) {
+ throw new IllegalStateException("Expected runFromNative to be done.");
+ }
+ if (!group.isDestroyed()) {
+ throw new IllegalStateException("Threadgroup should be destroyed.");
+ }
+ }
+
+ private static boolean executed = false;
+ private static void runFromNative() {
+ executed = true;
+ }
+ private static native void testNativeThread(ThreadGroup group);
+}
diff --git a/test/466-get-live-vreg/get_live_vreg_jni.cc b/test/466-get-live-vreg/get_live_vreg_jni.cc
index aeb9e44541..44ea0c9877 100644
--- a/test/466-get-live-vreg/get_live_vreg_jni.cc
+++ b/test/466-get-live-vreg/get_live_vreg_jni.cc
@@ -42,7 +42,8 @@ class TestVisitor : public StackVisitor {
CHECK(GetVReg(m, 0, kIntVReg, &value));
CHECK_EQ(value, 42u);
} else if (m_name.compare("$opt$noinline$testIntervalHole") == 0) {
- uint32_t number_of_dex_registers = CodeItemDataAccessor(m).RegistersSize();
+ uint32_t number_of_dex_registers =
+ CodeItemDataAccessor(m->DexInstructionData()).RegistersSize();
uint32_t dex_register_of_first_parameter = number_of_dex_registers - 2;
found_method_ = true;
uint32_t value = 0;
diff --git a/test/651-checker-int-simd-minmax/src/Main.java b/test/651-checker-int-simd-minmax/src/Main.java
index 66343adaa8..cfa0ae7dca 100644
--- a/test/651-checker-int-simd-minmax/src/Main.java
+++ b/test/651-checker-int-simd-minmax/src/Main.java
@@ -27,10 +27,10 @@ public class Main {
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Min>>] loop:<<Loop>> outer_loop:none
//
/// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMin(int[], int[], int[]) loop_optimization (after)
- /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] unsigned:false loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Int32 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Min>>] loop:<<Loop>> outer_loop:none
private static void doitMin(int[] x, int[] y, int[] z) {
int min = Math.min(x.length, Math.min(y.length, z.length));
for (int i = 0; i < min; i++) {
@@ -46,10 +46,10 @@ public class Main {
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Max>>] loop:<<Loop>> outer_loop:none
//
/// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMax(int[], int[], int[]) loop_optimization (after)
- /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] unsigned:false loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Int32 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>> outer_loop:none
private static void doitMax(int[] x, int[] y, int[] z) {
int min = Math.min(x.length, Math.min(y.length, z.length));
for (int i = 0; i < min; i++) {
diff --git a/test/674-HelloWorld-Dm/expected.txt b/test/674-HelloWorld-Dm/expected.txt
new file mode 100644
index 0000000000..af5626b4a1
--- /dev/null
+++ b/test/674-HelloWorld-Dm/expected.txt
@@ -0,0 +1 @@
+Hello, world!
diff --git a/test/674-HelloWorld-Dm/info.txt b/test/674-HelloWorld-Dm/info.txt
new file mode 100644
index 0000000000..3a769c48a6
--- /dev/null
+++ b/test/674-HelloWorld-Dm/info.txt
@@ -0,0 +1 @@
+Hello World test with --dm-file passed to dex2oat.
diff --git a/test/674-HelloWorld-Dm/run b/test/674-HelloWorld-Dm/run
new file mode 100644
index 0000000000..199ffc31e1
--- /dev/null
+++ b/test/674-HelloWorld-Dm/run
@@ -0,0 +1,17 @@
+#!/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.
+
+exec ${RUN} --dm "${@}"
diff --git a/test/674-HelloWorld-Dm/src/Main.java b/test/674-HelloWorld-Dm/src/Main.java
new file mode 100644
index 0000000000..1ef6289559
--- /dev/null
+++ b/test/674-HelloWorld-Dm/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ }
+}
diff --git a/test/674-hotness-compiled/expected.txt b/test/674-hotness-compiled/expected.txt
new file mode 100644
index 0000000000..6a5618ebc6
--- /dev/null
+++ b/test/674-hotness-compiled/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/674-hotness-compiled/info.txt b/test/674-hotness-compiled/info.txt
new file mode 100644
index 0000000000..e2cf59a093
--- /dev/null
+++ b/test/674-hotness-compiled/info.txt
@@ -0,0 +1 @@
+Test for the --count-hotness-in-compiled-code compiler option.
diff --git a/test/674-hotness-compiled/run b/test/674-hotness-compiled/run
new file mode 100755
index 0000000000..85e8e3b13f
--- /dev/null
+++ b/test/674-hotness-compiled/run
@@ -0,0 +1,17 @@
+#!/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.
+
+${RUN} "$@" -Xcompiler-option --count-hotness-in-compiled-code
diff --git a/test/674-hotness-compiled/src/Main.java b/test/674-hotness-compiled/src/Main.java
new file mode 100644
index 0000000000..76ec92777f
--- /dev/null
+++ b/test/674-hotness-compiled/src/Main.java
@@ -0,0 +1,46 @@
+/*
+ * 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 $noinline$hotnessCount() {
+ }
+
+ public static void $noinline$hotnessCountWithLoop() {
+ for (int i = 0; i < 100; i++) {
+ $noinline$hotnessCount();
+ }
+ }
+
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ if (!isAotCompiled(Main.class, "main")) {
+ return;
+ }
+ $noinline$hotnessCount();
+ int counter = getHotnessCounter(Main.class, "$noinline$hotnessCount");
+ if (counter == 0) {
+ throw new Error("Expected hotness counter to be updated");
+ }
+
+ $noinline$hotnessCountWithLoop();
+ if (getHotnessCounter(Main.class, "$noinline$hotnessCountWithLoop") <= counter) {
+ throw new Error("Expected hotness counter of a loop to be greater than without loop");
+ }
+ }
+
+ public static native int getHotnessCounter(Class<?> cls, String methodName);
+ public static native boolean isAotCompiled(Class<?> cls, String methodName);
+}
diff --git a/test/913-heaps/expected_d8.diff b/test/913-heaps/expected_d8.diff
index 83694220a3..3ea3c0d2b0 100644
--- a/test/913-heaps/expected_d8.diff
+++ b/test/913-heaps/expected_d8.diff
@@ -39,37 +39,31 @@
---
> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1]
> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1]
-201,202c202,203
+201,202c202
< root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 30])--> 1@1000 [size=16, length=-1]
< root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1]
---
> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1]
-> root@root --(stack-local[id=1,tag=3000,depth=4,method=runFollowReferences,vreg=3,location= 164])--> 1000@0 [size=123456780050, length=-1]
-246c247
+246c246
< root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=11,location= 8])--> 1@1000 [size=16, length=-1]
---
> root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1]
-248c249,251
+248c248,249
< root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1]
---
> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1]
> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1]
-> root@root --(stack-local[id=1,tag=3000,depth=4,method=runFollowReferences,vreg=3,location= 164])--> 1000@0 [size=123456780055, length=-1]
-292c295
+292d292
< root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1]
----
-> root@root --(stack-local[id=1,tag=3000,depth=4,method=runFollowReferences,vreg=3,location= 181])--> 1000@0 [size=123456780060, length=-1]
-319a323
-> root@root --(stack-local[id=1,tag=3000,depth=4,method=runFollowReferences,vreg=3,location= 181])--> 1000@0 [size=123456780065, length=-1]
-347c351
+347c347
< root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 30])--> 1@1000 [size=16, length=-1]
---
> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1]
-366c370
+366c366
< root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=11,location= 8])--> 1@1000 [size=16, length=-1]
---
> root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1]
-368c372,373
+368c368,369
< root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1]
---
> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1]
diff --git a/test/959-invoke-polymorphic-accessors/src/Main.java b/test/959-invoke-polymorphic-accessors/src/Main.java
index cdde1de515..03fd285d46 100644
--- a/test/959-invoke-polymorphic-accessors/src/Main.java
+++ b/test/959-invoke-polymorphic-accessors/src/Main.java
@@ -901,6 +901,10 @@ public class Main {
fail();
} catch (WrongMethodTypeException expected) {}
try {
+ h0.invoke(Double.valueOf(0.33));
+ fail();
+ } catch (WrongMethodTypeException expected) {}
+ try {
Number doubleNumber = getDoubleAsNumber();
h0.invoke(doubleNumber);
fail();
diff --git a/test/983-source-transform-verify/expected.txt b/test/983-source-transform-verify/expected.txt
index abcdf3a868..aa51ea08ae 100644
--- a/test/983-source-transform-verify/expected.txt
+++ b/test/983-source-transform-verify/expected.txt
@@ -1,2 +1,3 @@
Dex file hook for art/Test983$Transform
Dex file hook for java/lang/Object
+Dex file hook for java/lang/ClassLoader
diff --git a/test/983-source-transform-verify/src/art/Test983.java b/test/983-source-transform-verify/src/art/Test983.java
index b81e7f4df3..faae96aef6 100644
--- a/test/983-source-transform-verify/src/art/Test983.java
+++ b/test/983-source-transform-verify/src/art/Test983.java
@@ -16,7 +16,6 @@
package art;
-import java.util.Base64;
public class Test983 {
static class Transform {
public void sayHi() {
@@ -29,10 +28,11 @@ public class Test983 {
}
public static void doTest() {
- Transform abc = new Transform();
Redefinition.enableCommonRetransformation(true);
Redefinition.doCommonClassRetransformation(Transform.class);
Redefinition.doCommonClassRetransformation(Object.class);
+ // NB java.lang.ClassLoader has hidden fields.
+ Redefinition.doCommonClassRetransformation(ClassLoader.class);
Redefinition.enableCommonRetransformation(false);
}
}
diff --git a/test/Android.bp b/test/Android.bp
index 470a68f386..72e8eee95a 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -62,6 +62,7 @@ art_cc_defaults {
"libvixld-arm",
"libvixld-arm64",
"libart-gtest",
+ "libdexfile",
"libbase",
"libicuuc",
@@ -113,6 +114,7 @@ art_cc_defaults {
shared_libs: [
"libartd",
"libartd-compiler",
+ "libdexfile",
],
static_libs: [
"libgtest",
@@ -149,6 +151,7 @@ art_cc_library {
shared_libs: [
"libartd",
"libartd-compiler",
+ "libdexfile",
"libbase",
"libbacktrace",
],
@@ -166,6 +169,7 @@ cc_defaults {
"art_defaults",
],
shared_libs: [
+ "libdexfile",
"libbacktrace",
"libbase",
"libnativehelper",
@@ -264,6 +268,7 @@ art_cc_defaults {
"1943-suspend-raw-monitor-wait/native_suspend_monitor.cc",
],
shared_libs: [
+ "libdexfile",
"libbase",
],
header_libs: [
@@ -368,6 +373,7 @@ cc_defaults {
"149-suspend-all-stress/suspend_all.cc",
"154-gc-loop/heap_interface.cc",
"167-visit-locks/visit_locks.cc",
+ "169-threadgroup-jni/jni_daemon_thread.cc",
"203-multi-checkpoint/multi_checkpoint.cc",
"305-other-fault-handler/fault_handler.cc",
"454-get-vreg/get_vreg_jni.cc",
@@ -393,6 +399,7 @@ cc_defaults {
"708-jit-cache-churn/jit.cc",
],
shared_libs: [
+ "libdexfile",
"libbacktrace",
"libbase",
"libnativehelper",
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index c2408b0d2f..298a2f0033 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -251,13 +251,6 @@ extern "C" JNIEXPORT int JNICALL Java_Main_getHotnessCounter(JNIEnv* env,
jclass,
jclass cls,
jstring method_name) {
- jit::Jit* jit = Runtime::Current()->GetJit();
- if (jit == nullptr) {
- // The hotness counter is valid only under JIT.
- // If we don't JIT return 0 to match test expectations.
- return 0;
- }
-
ArtMethod* method = nullptr;
{
ScopedObjectAccess soa(Thread::Current());
@@ -297,4 +290,25 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isClassMoveable(JNIEnv*,
return runtime->GetHeap()->IsMovableObject(klass);
}
+extern "C" JNIEXPORT void JNICALL Java_Main_waitForCompilation(JNIEnv*, jclass) {
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit != nullptr) {
+ jit->WaitForCompilationToFinish(Thread::Current());
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_stopJit(JNIEnv*, jclass) {
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit != nullptr) {
+ jit->Stop();
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_startJit(JNIEnv*, jclass) {
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit != nullptr) {
+ jit->Start();
+ }
+}
+
} // namespace art
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 5e40b86aa0..ea2d464d24 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -64,6 +64,7 @@ ARGS=""
EXTERNAL_LOG_TAGS="n" # if y respect externally set ANDROID_LOG_TAGS.
DRY_RUN="n" # if y prepare to run the test but don't run it.
TEST_VDEX="n"
+TEST_DM="n"
TEST_IS_NDEBUG="n"
APP_IMAGE="y"
JVMTI_STRESS="n"
@@ -346,6 +347,9 @@ while true; do
elif [ "x$1" = "x--vdex" ]; then
TEST_VDEX="y"
shift
+ elif [ "x$1" = "x--dm" ]; then
+ TEST_DM="y"
+ shift
elif [ "x$1" = "x--vdex-filter" ]; then
shift
option="$1"
@@ -436,7 +440,13 @@ if [ "$DEBUGGER" = "y" ]; then
msg " adb forward tcp:$PORT tcp:$PORT"
fi
msg " jdb -attach localhost:$PORT"
- DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
+ if [ "$USE_JVM" = "n" ]; then
+ # TODO We should switch over to using the jvmti agent by default.
+ # Need to tell the runtime to enable the internal jdwp implementation.
+ DEBUGGER_OPTS="-XjdwpOptions:transport=dt_socket,address=$PORT,server=y,suspend=y -XjdwpProvider:internal"
+ else
+ DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
+ fi
elif [ "$DEBUGGER" = "agent" ]; then
PORT=12345
# TODO Support ddms connection and support target.
@@ -672,6 +682,7 @@ fi
profman_cmdline="true"
dex2oat_cmdline="true"
vdex_cmdline="true"
+dm_cmdline="true"
mkdir_locations="${mkdir_locations} ${DEX_LOCATION}/dalvik-cache/$ISA"
strip_cmdline="true"
sync_cmdline="true"
@@ -735,6 +746,10 @@ if [ "$PREBUILD" = "y" ]; then
vdex_cmdline="${dex2oat_cmdline} ${VDEX_FILTER} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex --output-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex"
elif [ "$TEST_VDEX" = "y" ]; then
vdex_cmdline="${dex2oat_cmdline} ${VDEX_FILTER} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex"
+ elif [ "$TEST_DM" = "y" ]; then
+ dex2oat_cmdline="${dex2oat_cmdline} --output-vdex=$DEX_LOCATION/oat/$ISA/primary.vdex"
+ dm_cmdline="zip -qj $DEX_LOCATION/oat/$ISA/$TEST_NAME.dm $DEX_LOCATION/oat/$ISA/primary.vdex"
+ vdex_cmdline="${dex2oat_cmdline} --dump-timings --dm-file=$DEX_LOCATION/oat/$ISA/$TEST_NAME.dm"
elif [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then
vdex_cmdline="${dex2oat_cmdline} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex --output-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex"
fi
@@ -782,6 +797,7 @@ dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ROOT/bin/$DALVIKVM \
# Remove whitespace.
dex2oat_cmdline=$(echo $dex2oat_cmdline)
dalvikvm_cmdline=$(echo $dalvikvm_cmdline)
+dm_cmdline=$(echo $dm_cmdline)
vdex_cmdline=$(echo $vdex_cmdline)
profman_cmdline=$(echo $profman_cmdline)
@@ -851,6 +867,7 @@ if [ "$HOST" = "n" ]; then
export PATH=$ANDROID_ROOT/bin:$PATH && \
$profman_cmdline && \
$dex2oat_cmdline && \
+ $dm_cmdline && \
$vdex_cmdline && \
$strip_cmdline && \
$sync_cmdline && \
@@ -927,7 +944,7 @@ else
fi
if [ "$DEV_MODE" = "y" ]; then
- echo "mkdir -p ${mkdir_locations} && $profman_cmdline && $dex2oat_cmdline && $vdex_cmdline && $strip_cmdline && $sync_cmdline && $cmdline"
+ echo "mkdir -p ${mkdir_locations} && $profman_cmdline && $dex2oat_cmdline && $dm_cmdline && $vdex_cmdline && $strip_cmdline && $sync_cmdline && $cmdline"
fi
cd $ANDROID_BUILD_TOP
@@ -943,6 +960,7 @@ else
mkdir -p ${mkdir_locations} || exit 1
$profman_cmdline || { echo "Profman failed." >&2 ; exit 2; }
$dex2oat_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
+ $dm_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
$vdex_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
$strip_cmdline || { echo "Strip failed." >&2 ; exit 3; }
$sync_cmdline || { echo "Sync failed." >&2 ; exit 4; }
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 27bec3e965..83ac068109 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -648,9 +648,18 @@
"description": ["Tests that depend on input-vdex are not supported with compact dex"]
},
{
+ "tests": ["137-cfi"],
+ "variant": "cdex-fast",
+ "description": ["Temporarily disabled until libbacktrace works properly with shared data section"]
+ },
+ {
"tests": "661-oat-writer-layout",
"variant": "interp-ac | interpreter | jit | no-dex2oat | no-prebuild | no-image | trace | redefine-stress | jvmti-stress",
"description": ["Test is designed to only check --compiler-filter=speed"]
+ },
+ {
+ "tests": "674-HelloWorld-Dm",
+ "variant": "target",
+ "description": ["Requires zip, which isn't available on device"]
}
-
]
diff --git a/test/run-test b/test/run-test
index a453f22e29..6bcb9cdabb 100755
--- a/test/run-test
+++ b/test/run-test
@@ -427,6 +427,9 @@ while true; do
elif [ "x$1" = "x--vdex" ]; then
run_args="${run_args} --vdex"
shift
+ elif [ "x$1" = "x--dm" ]; then
+ run_args="${run_args} --dm"
+ shift
elif [ "x$1" = "x--vdex-filter" ]; then
shift
filter=$1
diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py
index 2bb407db58..3064c76ffd 100644
--- a/test/testrunner/target_config.py
+++ b/test/testrunner/target_config.py
@@ -230,7 +230,7 @@ target_config = {
'art-heap-poisoning' : {
'run-test' : ['--interpreter',
'--optimizing',
- '--cdex-fast'],
+ '--cdex-none'],
'env' : {
'ART_USE_READ_BARRIER' : 'false',
'ART_HEAP_POISONING' : 'true',
@@ -327,27 +327,20 @@ target_config = {
'ART_USE_READ_BARRIER' : 'false'
}
},
- 'art-gtest-heap-poisoning': {
- 'make' : 'valgrind-test-art-host64',
- 'env' : {
- 'ART_HEAP_POISONING' : 'true',
- 'ART_USE_READ_BARRIER' : 'false'
- }
- },
# ASAN (host) configurations.
# These configurations need detect_leaks=0 to work in non-setup environments like build bots,
# as our build tools leak. b/37751350
- 'art-gtest-asan': {
+ 'art-gtest-asan': {
'make' : 'test-art-host-gtest',
'env': {
'SANITIZE_HOST' : 'address',
'ASAN_OPTIONS' : 'detect_leaks=0'
}
- },
- 'art-asan': {
+ },
+ 'art-asan': {
'run-test' : ['--interpreter',
'--optimizing',
'--jit'],
@@ -355,7 +348,16 @@ target_config = {
'SANITIZE_HOST' : 'address',
'ASAN_OPTIONS' : 'detect_leaks=0'
}
- },
+ },
+ 'art-gtest-heap-poisoning': {
+ 'make' : 'test-art-host-gtest',
+ 'env' : {
+ 'ART_HEAP_POISONING' : 'true',
+ 'ART_USE_READ_BARRIER' : 'false',
+ 'SANITIZE_HOST' : 'address',
+ 'ASAN_OPTIONS' : 'detect_leaks=0'
+ }
+ },
# ART Golem build targets used by go/lem (continuous ART benchmarking),
# (art-opt-cc is used by default since it mimics the default preopt config),
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 93998579f3..8a1e06cabf 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -185,7 +185,7 @@ def setup_test_env():
_user_input_variants['prebuild'].add('prebuild')
if not _user_input_variants['cdex_level']: # Default
- _user_input_variants['cdex_level'].add('cdex-none')
+ _user_input_variants['cdex_level'].add('cdex-fast')
# By default only run without jvmti
if not _user_input_variants['jvmti']:
diff --git a/tools/ahat/Android.mk b/tools/ahat/Android.mk
index 34e6a9cd42..bf79751659 100644
--- a/tools/ahat/Android.mk
+++ b/tools/ahat/Android.mk
@@ -123,7 +123,7 @@ $(AHAT_TEST_DUMP_PROGUARD_MAP): $(proguard_dictionary)
# Run ahat-test-dump.jar to generate test-dump.hprof and test-dump-base.hprof
AHAT_TEST_DUMP_DEPENDENCIES := \
$(HOST_OUT_EXECUTABLES)/dalvikvm64 \
- $(ART_HOST_SHARED_LIBRARY_DEPENDENCIES) \
+ $(ART_HOST_SHARED_LIBRARY_DEBUG_DEPENDENCIES) \
$(HOST_OUT_EXECUTABLES)/art \
$(HOST_CORE_IMG_OUT_BASE)$(CORE_IMG_SUFFIX)
@@ -134,7 +134,7 @@ $(AHAT_TEST_DUMP_HPROF): $(AHAT_TEST_DUMP_JAR) $(AHAT_TEST_DUMP_DEPENDENCIES)
rm -rf $(PRIVATE_AHAT_TEST_ANDROID_DATA)
mkdir -p $(PRIVATE_AHAT_TEST_ANDROID_DATA)
ANDROID_DATA=$(PRIVATE_AHAT_TEST_ANDROID_DATA) \
- $(PRIVATE_AHAT_TEST_ART) --64 -cp $(PRIVATE_AHAT_TEST_DUMP_JAR) Main $@
+ $(PRIVATE_AHAT_TEST_ART) -d --64 -cp $(PRIVATE_AHAT_TEST_DUMP_JAR) Main $@
$(AHAT_TEST_DUMP_BASE_HPROF): PRIVATE_AHAT_TEST_ART := $(HOST_OUT_EXECUTABLES)/art
$(AHAT_TEST_DUMP_BASE_HPROF): PRIVATE_AHAT_TEST_DUMP_JAR := $(AHAT_TEST_DUMP_JAR)
@@ -143,7 +143,7 @@ $(AHAT_TEST_DUMP_BASE_HPROF): $(AHAT_TEST_DUMP_JAR) $(AHAT_TEST_DUMP_DEPENDENCIE
rm -rf $(PRIVATE_AHAT_TEST_ANDROID_DATA)
mkdir -p $(PRIVATE_AHAT_TEST_ANDROID_DATA)
ANDROID_DATA=$(PRIVATE_AHAT_TEST_ANDROID_DATA) \
- $(PRIVATE_AHAT_TEST_ART) --64 -cp $(PRIVATE_AHAT_TEST_DUMP_JAR) Main $@ --base
+ $(PRIVATE_AHAT_TEST_ART) -d --64 -cp $(PRIVATE_AHAT_TEST_DUMP_JAR) Main $@ --base
# --- ahat-tests.jar --------------
include $(CLEAR_VARS)
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index fd9ad0bb0f..8956e98ed0 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -74,7 +74,14 @@ if [[ $mode == "host" ]]; then
make_command+=" dx-tests"
mode_suffix="-host"
elif [[ $mode == "target" ]]; then
- make_command="make $j_arg $extra_args $showcommands build-art-target-tests $common_targets"
+ # Create dummy hidden API lists which are normally generated by the framework
+ # but which we do not have in the buildbot manifest. These are empty because
+ # we do not want to enforce these rules in the buildbots anyway.
+ hiddenapi_out_dir=${out_dir}/target/common/obj/PACKAGING
+ make_command="mkdir -p ${hiddenapi_out_dir} && "
+ make_command+="touch ${hiddenapi_out_dir}/hiddenapi-{blacklist,dark-greylist,light-greylist}.txt && "
+
+ make_command+="make $j_arg $extra_args $showcommands build-art-target-tests $common_targets"
make_command+=" libjavacrypto-target libnetd_client-target linker toybox toolbox sh"
make_command+=" ${out_dir}/host/linux-x86/bin/adb libstdc++ "
make_command+=" ${out_dir}/target/product/${TARGET_PRODUCT}/system/etc/public.libraries.txt"
@@ -89,4 +96,4 @@ done
echo "Executing $make_command"
-$make_command
+bash -c "$make_command"
diff --git a/tools/dt_fds_forward.py b/tools/dt_fds_forward.py
index 516b7fef96..1f9c41fc26 100755
--- a/tools/dt_fds_forward.py
+++ b/tools/dt_fds_forward.py
@@ -34,10 +34,11 @@ import subprocess
import sys
import time
-LISTEN_START_MESSAGE = b"dt_fd_forward:START-LISTEN\x00"
-LISTEN_END_MESSAGE = b"dt_fd_forward:END-LISTEN\x00"
-ACCEPTED_MESSAGE = b"dt_fd_forward:ACCEPTED\x00"
-CLOSE_MESSAGE = b"dt_fd_forward:CLOSING\x00"
+NEED_HANDSHAKE_MESSAGE = b"HANDSHAKE:REQD\x00"
+LISTEN_START_MESSAGE = b"dt_fd_forward:START-LISTEN\x00"
+LISTEN_END_MESSAGE = b"dt_fd_forward:END-LISTEN\x00"
+ACCEPTED_MESSAGE = b"dt_fd_forward:ACCEPTED\x00"
+CLOSE_MESSAGE = b"dt_fd_forward:CLOSING\x00"
libc = ctypes.cdll.LoadLibrary("libc.so.6")
def eventfd(init_val, flags):
@@ -70,7 +71,7 @@ def send_fds(sock, remote_read, remote_write, remote_event):
"""
Send the three fds over the given socket.
"""
- sock.sendmsg([b"!"], # We don't actually care what we send here.
+ sock.sendmsg([NEED_HANDSHAKE_MESSAGE], # We want the transport to handle the handshake.
[(socket.SOL_SOCKET, # Send over socket.
socket.SCM_RIGHTS, # Payload is file-descriptor array
array.array('i', [remote_read, remote_write, remote_event]))])
diff --git a/tools/generate-boot-image-profile.sh b/tools/generate-boot-image-profile.sh
index d87123ad71..ee53f43865 100755
--- a/tools/generate-boot-image-profile.sh
+++ b/tools/generate-boot-image-profile.sh
@@ -46,7 +46,9 @@ for file in "$@"; do
fi
done
-jar_args=()
+# Boot jars have hidden API access flags which do not pass dex file
+# verification. Skip it.
+jar_args=("--skip-apk-verification")
boot_jars=$("$ANDROID_BUILD_TOP"/art/tools/bootjars.sh --target)
jar_dir=$ANDROID_BUILD_TOP/$(get_build_var TARGET_OUT_JAVA_LIBRARIES)
for file in $boot_jars; do
diff --git a/tools/hiddenapi/Android.bp b/tools/hiddenapi/Android.bp
index a78bc43aa4..f9824f1fa3 100644
--- a/tools/hiddenapi/Android.bp
+++ b/tools/hiddenapi/Android.bp
@@ -30,6 +30,7 @@ cc_defaults {
},
shared_libs: [
+ "libdexfile",
"libbase",
],
}
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc
index a755fdb40b..c893da646d 100644
--- a/tools/hiddenapi/hiddenapi.cc
+++ b/tools/hiddenapi/hiddenapi.cc
@@ -118,9 +118,11 @@ class DexMember {
// until we hit the terminating byte of the previous Leb128 value.
const uint8_t* ptr = it_.DataPointer();
if (it_.IsAtMethod()) {
- ptr = ReverseSearchUnsignedLeb128(ptr, it_.GetMethodCodeItemOffset());
+ ptr = ReverseSearchUnsignedLeb128(ptr);
+ DCHECK_EQ(DecodeUnsignedLeb128WithoutMovingCursor(ptr), it_.GetMethodCodeItemOffset());
}
- ptr = ReverseSearchUnsignedLeb128(ptr, old_flags);
+ ptr = ReverseSearchUnsignedLeb128(ptr);
+ DCHECK_EQ(DecodeUnsignedLeb128WithoutMovingCursor(ptr), old_flags);
// Overwrite the access flags.
UpdateUnsignedLeb128(const_cast<uint8_t*>(ptr), new_flags);
@@ -158,38 +160,6 @@ class DexMember {
return klass_.GetDexFile().GetFieldId(it_.GetMemberIndex());
}
- static inline bool IsLeb128Terminator(const uint8_t* ptr) {
- return *ptr <= 0x7f;
- }
-
- // Returns the first byte of a Leb128 value assuming that:
- // (1) `end_ptr` points to the first byte after the Leb128 value, and
- // (2) there is another Leb128 value before this one.
- // The function will fail after reading 5 bytes (the longest supported Leb128
- // encoding) to protect against situations when (2) is not satisfied.
- // When a Leb128 value is discovered, it is decoded and CHECKed against `value`.
- static const uint8_t* ReverseSearchUnsignedLeb128(const uint8_t* end_ptr, uint32_t expected) {
- const uint8_t* ptr = end_ptr;
-
- // Move one byte back, check that this is the terminating byte.
- ptr--;
- CHECK(IsLeb128Terminator(ptr));
-
- // Keep moving back while the previous byte is not a terminating byte.
- // Fail after reading five bytes in case there isn't another Leb128 value
- // before this one.
- while (!IsLeb128Terminator(ptr - 1)) {
- ptr--;
- CHECK_LE((size_t) (end_ptr - ptr), 5u);
- }
-
- // Check that the decoded value matches the `expected` value.
- const uint8_t* tmp_ptr = ptr;
- CHECK_EQ(DecodeUnsignedLeb128(&tmp_ptr), expected);
-
- return ptr;
- }
-
const DexClass& klass_;
const ClassDataItemIterator& it_;
};
diff --git a/tools/jfuzz/run_jfuzz_test.py b/tools/jfuzz/run_jfuzz_test.py
index bddce32ad5..4a54a3a4f2 100755
--- a/tools/jfuzz/run_jfuzz_test.py
+++ b/tools/jfuzz/run_jfuzz_test.py
@@ -169,7 +169,7 @@ class TestRunnerRIOnHost(TestRunner):
def CompileAndRunTest(self):
dbg = '-g' if self._debug_info else '-g:none'
- if RunCommand(['javac', dbg, 'Test.java'],
+ if RunCommand(['javac', '--release=8', dbg, 'Test.java'],
out=None, err=None, timeout=30) == RetCode.SUCCESS:
retc = RunCommand(['java', 'Test'], self.output_file, err=None)
else:
diff --git a/tools/prebuilt_libjdwp_art_failures.txt b/tools/prebuilt_libjdwp_art_failures.txt
index 6052f237a7..7a8a4dd35f 100644
--- a/tools/prebuilt_libjdwp_art_failures.txt
+++ b/tools/prebuilt_libjdwp_art_failures.txt
@@ -123,6 +123,7 @@
description: "Test is flakey",
result: EXEC_FAILED,
bug: 70958370,
- name: "org.apache.harmony.jpda.tests.jdwp.ObjectReference.EnableCollectionTest#testEnableCollection001"
+ names: [ "org.apache.harmony.jpda.tests.jdwp.ObjectReference.EnableCollectionTest#testEnableCollection001",
+ "org.apache.harmony.jpda.tests.jdwp.MultiSession.EnableCollectionTest#testEnableCollection001" ]
}
]
diff --git a/tools/public.libraries.buildbot.txt b/tools/public.libraries.buildbot.txt
index 4b01796a0a..734fd1e50b 100644
--- a/tools/public.libraries.buildbot.txt
+++ b/tools/public.libraries.buildbot.txt
@@ -1,5 +1,6 @@
libart.so
libartd.so
+libdexfile.so
libbacktrace.so
libc.so
libc++.so
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index 2cf614d795..b512612175 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -286,6 +286,10 @@ fi
if [[ $mode != "ri" ]]; then
toolchain_args="--toolchain d8 --language CUR"
+ if [[ "x$with_jdwp_path" == "x" ]]; then
+ # Need to enable the internal jdwp implementation.
+ art_debugee="${art_debugee} -XjdwpProvider:internal"
+ fi
else
toolchain_args="--toolchain javac --language CUR"
fi