Have adbconnection handle DDMS packets if agent not loaded
If DDMS was being used on a userdebug or eng build it would cause the
libjdwp agent to be loaded on various system processes, most notably
system_server. This would cause a massive performance hit as it tries
to force system_server to jit but, due to security policies, it is
forced to interpreter instead.
To fix this we make it so that (so long as no debugger commands are
issued) DDMS commands are handled without loading the JDWP agent. When
a non-DDMS command is issued we will load and initialize the JDWP
agent which will take over handling DDMS traffic from then on.
This will ensure that in the common (for userdebug) use-case where
processes only encounter DDMS commands the process will not need to
load the full debugger.
Test: ./test.py --host -j50
Test: ./art/tools/run-libjdwp-tests.sh --mode=device
Test: Run ddms monitor on host,
adb shell stop &&
adb shell setprop dalvik.vm.jdwp-provider adbconnection
adb shell start;
Ensure that device does not start to jank
Test: Run ddms monitor on host,
adb shell stop &&
adb shell setprop dalvik.vm.jdwp-provider adbconnection
adb shell start;
Turn off ddms monitor.
Ensure that device does not start to jank
Test: Build and run
Test: use ddms monitor.
Test: Use Android Studio.
Test: Build and debug debuggable app (bandhook-kotlin)
Test: Build and debug non-debuggable app on userdebug build
(bandhook-kotlin)
Test: Debug running system process on userdebug build
(com.android.packageinstaller)
Bug: 62821960
Bug: 72456312
Bug: 72457427
Change-Id: Ib8d2af6c76bd2fac5a4b27e730695b2d016d3583
diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc
index 80cfc83..56677c9 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,6 +135,10 @@
shutting_down_(false),
agent_loaded_(false),
agent_listening_(false),
+ agent_has_socket_(false),
+ sent_agent_fds_(false),
+ performed_handshake_(false),
+ notified_ddm_active_(false),
next_ddm_id_(1) {
// Setup the addr.
control_addr_.controlAddrUn.sun_family = AF_UNIX;
@@ -245,10 +268,32 @@
}
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 +302,13 @@
}
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 +328,7 @@
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 +336,35 @@
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 +392,16 @@
}
}
-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;
@@ -492,6 +556,7 @@
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 +566,8 @@
{ (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 +593,7 @@
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 +603,8 @@
} 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 +633,9 @@
}
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 +647,15 @@
} 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,10 +663,175 @@
}
}
+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;
+}
+
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() ? "" : ",")
+ + "ddm_already_active=" + (notified_ddm_active_ ? "y" : "n") + ","
+ "transport=dt_fd_forward,address=" + std::to_string(remote_agent_control_sock_);
}
diff --git a/adbconnection/adbconnection.h b/adbconnection/adbconnection.h
index 28a5a05..e63a3b6 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 @@
AdbConnectionState* connection_;
};
+enum class DdmPacketType : uint8_t { kReply = 0x80, kCmd = 0x00, };
+
struct AdbConnectionDdmCallback : public art::DdmCallback {
explicit AdbConnectionDdmCallback(AdbConnectionState* connection) : connection_(connection) {}
@@ -94,10 +97,23 @@
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,6 +155,10 @@
std::atomic<bool> sent_agent_fds_;
+ bool performed_handshake_;
+
+ bool notified_ddm_active_;
+
std::atomic<uint32_t> next_ddm_id_;
socklen_t control_addr_len_;
diff --git a/dt_fd_forward/dt_fd_forward.cc b/dt_fd_forward/dt_fd_forward.cc
index 1cf31a7..116cdf8 100644
--- a/dt_fd_forward/dt_fd_forward.cc
+++ b/dt_fd_forward/dt_fd_forward.cc
@@ -276,17 +276,16 @@
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 @@
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 @@
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 @@
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 9303c59..07a574b 100644
--- a/dt_fd_forward/dt_fd_forward.h
+++ b/dt_fd_forward/dt_fd_forward.h
@@ -105,7 +105,9 @@
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 245f0c2..144ac5c 100644
--- a/dt_fd_forward/export/fd_transport.h
+++ b/dt_fd_forward/export/fd_transport.h
@@ -47,6 +47,12 @@
}
};
+// 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/tools/dt_fds_forward.py b/tools/dt_fds_forward.py
index 516b7fe..1f9c41f 100755
--- a/tools/dt_fds_forward.py
+++ b/tools/dt_fds_forward.py
@@ -34,10 +34,11 @@
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 @@
"""
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]))])