diff options
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, + ®isters_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, - ®isters_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 |