Basic JDWP option parsing...
...and just enough code to silence all the UNIMPLEMENTED warnings you get
during normal usage.
Change-Id: I56820ac971b88581c4cb1f462c13331d8fa04c43
diff --git a/src/debugger.cc b/src/debugger.cc
index 46644e7..c8f3cec 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -16,8 +16,131 @@
#include "debugger.h"
+#include <sys/uio.h>
+
namespace art {
+// Was there a -Xrunjdwp or -agent argument on the command-line?
+static bool gJdwpConfigured = false;
+
+// Broken-down JDWP options. (Only valid if gJdwpConfigured is true.)
+static JDWP::JdwpTransportType gJdwpTransport;
+static bool gJdwpServer;
+static bool gJdwpSuspend;
+static std::string gJdwpHost;
+static int gJdwpPort;
+
+// Runtime JDWP state.
+static JDWP::JdwpState* gJdwpState = NULL;
+static bool gDebuggerConnected; // debugger or DDMS is connected.
+static bool gDebuggerActive; // debugger is making requests.
+
+/*
+ * Handle one of the JDWP name/value pairs.
+ *
+ * JDWP options are:
+ * help: if specified, show help message and bail
+ * transport: may be dt_socket or dt_shmem
+ * address: for dt_socket, "host:port", or just "port" when listening
+ * server: if "y", wait for debugger to attach; if "n", attach to debugger
+ * timeout: how long to wait for debugger to connect / listen
+ *
+ * Useful with server=n (these aren't supported yet):
+ * onthrow=<exception-name>: connect to debugger when exception thrown
+ * onuncaught=y|n: connect to debugger when uncaught exception thrown
+ * launch=<command-line>: launch the debugger itself
+ *
+ * The "transport" option is required, as is "address" if server=n.
+ */
+static bool ParseJdwpOption(const std::string& name, const std::string& value) {
+ if (name == "transport") {
+ if (value == "dt_socket") {
+ gJdwpTransport = JDWP::kJdwpTransportSocket;
+ } else if (value == "dt_android_adb") {
+ gJdwpTransport = JDWP::kJdwpTransportAndroidAdb;
+ } else {
+ LOG(ERROR) << "JDWP transport not supported: " << value;
+ return false;
+ }
+ } else if (name == "server") {
+ if (value == "n") {
+ gJdwpServer = false;
+ } else if (value == "y") {
+ gJdwpServer = true;
+ } else {
+ LOG(ERROR) << "JDWP option 'server' must be 'y' or 'n'";
+ return false;
+ }
+ } else if (name == "suspend") {
+ if (value == "n") {
+ gJdwpSuspend = false;
+ } else if (value == "y") {
+ gJdwpSuspend = true;
+ } else {
+ LOG(ERROR) << "JDWP option 'suspend' must be 'y' or 'n'";
+ return false;
+ }
+ } else if (name == "address") {
+ /* this is either <port> or <host>:<port> */
+ std::string port_string;
+ gJdwpHost.clear();
+ std::string::size_type colon = value.find(':');
+ if (colon != std::string::npos) {
+ gJdwpHost = value.substr(0, colon);
+ port_string = value.substr(colon + 1);
+ } else {
+ port_string = value;
+ }
+ if (port_string.empty()) {
+ LOG(ERROR) << "JDWP address missing port: " << value;
+ return false;
+ }
+ char* end;
+ long port = strtol(port_string.c_str(), &end, 10);
+ if (*end != '\0') {
+ LOG(ERROR) << "JDWP address has junk in port field: " << value;
+ return false;
+ }
+ gJdwpPort = port;
+ } else if (name == "launch" || name == "onthrow" || name == "oncaught" || name == "timeout") {
+ /* valid but unsupported */
+ LOG(INFO) << "Ignoring JDWP option '" << name << "'='" << value << "'";
+ } else {
+ LOG(INFO) << "Ignoring unrecognized JDWP option '" << name << "'='" << value << "'";
+ }
+
+ return true;
+}
+
+/*
+ * Parse the latter half of a -Xrunjdwp/-agentlib:jdwp= string, e.g.:
+ * "transport=dt_socket,address=8000,server=y,suspend=n"
+ */
+bool Dbg::ParseJdwpOptions(const std::string& options) {
+ std::vector<std::string> pairs;
+ Split(options, ',', pairs);
+
+ for (size_t i = 0; i < pairs.size(); ++i) {
+ std::string::size_type equals = pairs[i].find('=');
+ if (equals == std::string::npos) {
+ LOG(ERROR) << "Can't parse JDWP option '" << pairs[i] << "' in '" << options << "'";
+ return false;
+ }
+ ParseJdwpOption(pairs[i].substr(0, equals), pairs[i].substr(equals + 1));
+ }
+
+ if (gJdwpTransport == JDWP::kJdwpTransportUnknown) {
+ LOG(ERROR) << "Must specify JDWP transport: " << options;
+ }
+ if (!gJdwpServer && (gJdwpHost.empty() || gJdwpPort == 0)) {
+ LOG(ERROR) << "Must specify JDWP host and port when server=n: " << options;
+ return false;
+ }
+
+ gJdwpConfigured = true;
+ return true;
+}
+
bool Dbg::DebuggerStartup() {
UNIMPLEMENTED(FATAL);
return false;
@@ -33,7 +156,9 @@
}
void Dbg::Connected() {
- UNIMPLEMENTED(FATAL);
+ CHECK(!gDebuggerConnected);
+ LOG(VERBOSE) << "JDWP has attached";
+ gDebuggerConnected = true;
}
void Dbg::Active() {
@@ -45,13 +170,11 @@
}
bool Dbg::IsDebuggerConnected() {
- UNIMPLEMENTED(WARNING);
- return false;
+ return gDebuggerActive;
}
bool Dbg::IsDebuggingEnabled() {
- UNIMPLEMENTED(WARNING);
- return false; //return gDvm.jdwpConfigured;
+ return gJdwpConfigured;
}
int64_t Dbg::LastDebuggerActivity() {
@@ -376,10 +499,16 @@
}
void Dbg::PostThreadStart(Thread* t) {
+ if (!gDebuggerConnected) {
+ return;
+ }
UNIMPLEMENTED(WARNING);
}
void Dbg::PostThreadDeath(Thread* t) {
+ if (!gDebuggerConnected) {
+ return;
+ }
UNIMPLEMENTED(WARNING);
}
@@ -431,12 +560,20 @@
UNIMPLEMENTED(FATAL);
}
-void Dbg::DdmSendChunk(int type, size_t length, const uint8_t* buf) {
- UNIMPLEMENTED(WARNING) << "DdmSendChunk(" << type << ", " << length << ", " << (void*) buf << ");";
+void Dbg::DdmSendChunk(int type, size_t byte_count, const uint8_t* buf) {
+ CHECK(buf != NULL);
+ iovec vec[1];
+ vec[0].iov_base = reinterpret_cast<void*>(const_cast<uint8_t*>(buf));
+ vec[0].iov_len = byte_count;
+ Dbg::DdmSendChunkV(type, vec, 1);
}
void Dbg::DdmSendChunkV(int type, const struct iovec* iov, int iovcnt) {
- UNIMPLEMENTED(FATAL);
+ if (gJdwpState == NULL) {
+ LOG(VERBOSE) << "Debugger thread not active, ignoring DDM send: " << type;
+ } else {
+ JDWP::DdmSendChunkV(gJdwpState, type, iov, iovcnt);
+ }
}
} // namespace art