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
diff --git a/src/debugger.h b/src/debugger.h
index b66fd11..388248b 100644
--- a/src/debugger.h
+++ b/src/debugger.h
@@ -23,8 +23,10 @@
#include <pthread.h>
-#include "object.h"
+#include <string>
+
#include "jdwp/jdwp.h"
+#include "object.h"
namespace art {
@@ -62,6 +64,7 @@
class Dbg {
public:
+ static bool ParseJdwpOptions(const std::string& options);
static bool DebuggerStartup();
static void DebuggerShutdown();
diff --git a/src/runtime.cc b/src/runtime.cc
index 08a1d9f..46ec8fb 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -7,10 +7,9 @@
#include <limits>
#include <vector>
-#include "ScopedLocalRef.h"
-#include "UniquePtr.h"
#include "class_linker.h"
#include "class_loader.h"
+#include "debugger.h"
#include "heap.h"
#include "image.h"
#include "intern_table.h"
@@ -22,6 +21,7 @@
#include "space.h"
#include "thread.h"
#include "thread_list.h"
+#include "UniquePtr.h"
// TODO: this drags in cutil/log.h, which conflicts with our logging.h.
#include "JniConstants.h"
@@ -255,6 +255,13 @@
parsed->images_.push_back(option.substr(strlen("-Ximage:")).data());
} else if (option.starts_with("-Xcheck:jni")) {
parsed->check_jni_ = true;
+ } else if (option.starts_with("-Xrunjdwp:") || option.starts_with("-agentlib:jdwp=")) {
+ std::string tail = option.substr(option[1] == 'X' ? 10 : 15).ToString();
+ if (tail == "help" || !Dbg::ParseJdwpOptions(tail)) {
+ LOG(FATAL) << "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n"
+ << "Example: -Xrunjdwp:transport=dt_socket,address=localhost:6500,server=n";
+ return NULL;
+ }
} else if (option.starts_with("-Xms")) {
size_t size = ParseMemoryOption(option.substr(strlen("-Xms")).data(), 1024);
if (size == 0) {
diff --git a/test/etc/host-run-test-jar b/test/etc/host-run-test-jar
index 1fba19d..3ac2688 100755
--- a/test/etc/host-run-test-jar
+++ b/test/etc/host-run-test-jar
@@ -116,5 +116,5 @@
fi
$INVOKE_WITH $gdb $exe $gdbargs "-Xbootclasspath:${bpath}" \
- $DEX_VERIFY $DEX_OPTIMIZE $DEX_DEBUG -ea \
+ $DEX_VERIFY $DEX_OPTIMIZE $DEX_DEBUG ${DEBUG_OPTS} -ea \
-cp test.jar Main "$@"
diff --git a/test/etc/push-and-run-test-jar b/test/etc/push-and-run-test-jar
index 858ba74..ec7bf46 100755
--- a/test/etc/push-and-run-test-jar
+++ b/test/etc/push-and-run-test-jar
@@ -102,13 +102,13 @@
fi
if [ "$DEBUG" = "y" ]; then
- DEX_DEBUG="-agentlib:jdwp=transport=dt_android_adb,server=y,suspend=y"
+ DEX_DEBUG="-agentlib:jdwp=transport=dt_android_adb,server=y,suspend=y"
fi
if [ "$ZYGOTE" = "y" ]; then
adb shell cd /data \; dvz -classpath $TEST_NAME.jar Main "$@"
else
- cmdline="cd /data; $INVOKE_WITH $OATEXEC -Xjnigreflimit:256 \
+ cmdline="cd /data; $INVOKE_WITH $OATEXEC ${DEX_DEBUG} -Xjnigreflimit:256 \
-Ximage:/data/art-test/core.art \
-cp /data/art-test/$TEST_NAME.jar \
Main"