ART: Ensure plugin is loaded on AttachAgent

Agents require the JVMTI plugin. Ensure that it is loaded when
trying to satisfy an AttachAgent request. Amend test 909.

Bug: 31682382
Test: m test-art-host-run-test-909-attach-agent
Change-Id: Id99d0315b5b2577167dd8f8448a052e04f3ed2e5
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 2086d70..df5fc5c 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1364,6 +1364,39 @@
   return true;
 }
 
+static bool EnsureJvmtiPlugin(Runtime* runtime,
+                              std::vector<Plugin>* plugins,
+                              std::string* error_msg) {
+  constexpr const char* plugin_name = kIsDebugBuild ? "libopenjdkjvmtid.so" : "libopenjdkjvmti.so";
+
+  // Is the plugin already loaded?
+  for (Plugin p : *plugins) {
+    if (p.GetLibrary() == plugin_name) {
+      return true;
+    }
+  }
+
+  // Is the process debuggable? Otherwise, do not attempt to load the plugin.
+  if (!runtime->IsDebuggable()) {
+    *error_msg = "Process is not debuggable.";
+    return false;
+  }
+
+  Plugin new_plugin = Plugin::Create(plugin_name);
+
+  // Suspend all threads to protect ourself somewhat.
+  Thread* self = Thread::Current();
+  ScopedObjectAccess soa(self);      // Now we know we have the shared lock.
+  ScopedThreadSuspension sts(self, art::kWaitingForDebuggerToAttach);
+  ScopedSuspendAll ssa("EnsureJvmtiPlugin");
+  if (!new_plugin.Load(error_msg)) {
+    return false;
+  }
+
+  plugins->push_back(std::move(new_plugin));
+  return true;
+}
+
 // Attach a new agent and add it to the list of runtime agents
 //
 // TODO: once we decide on the threading model for agents,
@@ -1371,18 +1404,25 @@
 //   (and we synchronize access to any shared data structures like "agents_")
 //
 void Runtime::AttachAgent(const std::string& agent_arg) {
+  std::string error_msg;
+  if (!EnsureJvmtiPlugin(this, &plugins_, &error_msg)) {
+    LOG(WARNING) << "Could not load plugin: " << error_msg;
+    ScopedObjectAccess soa(Thread::Current());
+    ThrowIOException("%s", error_msg.c_str());
+    return;
+  }
+
   ti::Agent agent(agent_arg);
 
   int res = 0;
-  std::string err;
-  ti::Agent::LoadError result = agent.Attach(&res, &err);
+  ti::Agent::LoadError result = agent.Attach(&res, &error_msg);
 
   if (result == ti::Agent::kNoError) {
     agents_.push_back(std::move(agent));
   } else {
-    LOG(ERROR) << "Agent attach failed (result=" << result << ") : " << err;
+    LOG(WARNING) << "Agent attach failed (result=" << result << ") : " << error_msg;
     ScopedObjectAccess soa(Thread::Current());
-    ThrowWrappedIOException("%s", err.c_str());
+    ThrowIOException("%s", error_msg.c_str());
   }
 }
 
diff --git a/test/909-attach-agent/expected.txt b/test/909-attach-agent/expected.txt
index eacc595..c0bccd6 100644
--- a/test/909-attach-agent/expected.txt
+++ b/test/909-attach-agent/expected.txt
@@ -1,3 +1,11 @@
 Hello, world!
 Attached Agent for test 909-attach-agent
 Goodbye!
+Hello, world!
+Attached Agent for test 909-attach-agent
+Goodbye!
+Hello, world!
+java.io.IOException: Process is not debuggable.
+	at dalvik.system.VMDebug.attachAgent(Native Method)
+	at Main.main(Main.java:27)
+Goodbye!
diff --git a/test/909-attach-agent/run b/test/909-attach-agent/run
index aed6e83..985341b 100755
--- a/test/909-attach-agent/run
+++ b/test/909-attach-agent/run
@@ -24,4 +24,14 @@
 ./default-run "$@" --experimental agents \
                    --experimental runtime-plugins \
                    --android-runtime-option -Xplugin:${plugin} \
+                   --android-runtime-option -Xfully-deoptable \
+                   --args agent:${agent}=909-attach-agent
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --android-runtime-option -Xfully-deoptable \
+                   --args agent:${agent}=909-attach-agent
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
                    --args agent:${agent}=909-attach-agent