Enable agent attaching during live phase
This is the ART part, the plumbing from VMDebug_attachAgent() to
actually loading the agent into the runtime.
Test: m test-art-host
Bug: 31682382
Change-Id: I3ccc67aa050c1f78278882128983686ed44ddec2
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 72d7df3..13a3235 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -407,7 +407,7 @@
Result ParseAndAppend(const std::string& args,
std::vector<ti::Agent>& existing_value) {
- existing_value.push_back(ti::Agent::Create(args));
+ existing_value.emplace_back(args);
return Result::SuccessNoValue();
}
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index b0aba59..0251776 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -686,6 +686,15 @@
va_end(args);
}
+// SecurityException
+
+void ThrowSecurityException(const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ ThrowException("Ljava/lang/SecurityException;", nullptr, fmt, &args);
+ va_end(args);
+}
+
// Stack overflow.
void ThrowStackOverflowError(Thread* self) {
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index 5d0bc12..76ea2ae 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -215,6 +215,12 @@
__attribute__((__format__(__printf__, 1, 2)))
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+// SecurityException
+
+void ThrowSecurityException(const char* fmt, ...)
+ __attribute__((__format__(__printf__, 1, 2)))
+ REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
// Stack overflow.
void ThrowStackOverflowError(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index e2b8f51..1da888e 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -551,6 +551,10 @@
gJdwpAllowed = allowed;
}
+bool Dbg::IsJdwpAllowed() {
+ return gJdwpAllowed;
+}
+
DebugInvokeReq* Dbg::GetInvokeReq() {
return Thread::Current()->GetInvokeReq();
}
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 5d0315e..3b4a5e1 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -202,6 +202,7 @@
class Dbg {
public:
static void SetJdwpAllowed(bool allowed);
+ static bool IsJdwpAllowed();
static void StartJdwp();
static void StopJdwp();
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 1852956..8d85425 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -482,6 +482,31 @@
return result;
}
+static void VMDebug_attachAgent(JNIEnv* env, jclass, jstring agent) {
+ if (agent == nullptr) {
+ ScopedObjectAccess soa(env);
+ ThrowNullPointerException("agent is null");
+ return;
+ }
+
+ if (!Dbg::IsJdwpAllowed()) {
+ ScopedObjectAccess soa(env);
+ ThrowSecurityException("Can't attach agent, process is not debuggable.");
+ return;
+ }
+
+ std::string filename;
+ {
+ ScopedUtfChars chars(env, agent);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ filename = chars.c_str();
+ }
+
+ Runtime::Current()->AttachAgent(filename);
+}
+
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"),
NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"),
@@ -514,7 +539,8 @@
NATIVE_METHOD(VMDebug, stopMethodTracing, "()V"),
NATIVE_METHOD(VMDebug, threadCpuTimeNanos, "!()J"),
NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"),
- NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;")
+ NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;"),
+ NATIVE_METHOD(VMDebug, attachAgent, "(Ljava/lang/String;)V"),
};
void register_dalvik_system_VMDebug(JNIEnv* env) {
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index f937ca7..56eab5e 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -601,7 +601,7 @@
<< "runtime plugins.";
} else if (!args.GetOrDefault(M::Plugins).empty()) {
LOG(WARNING) << "Experimental runtime plugin support has not been enabled. Ignored options: ";
- for (auto& op : args.GetOrDefault(M::Plugins)) {
+ for (const auto& op : args.GetOrDefault(M::Plugins)) {
LOG(WARNING) << " -plugin:" << op.GetLibrary();
}
}
@@ -614,14 +614,14 @@
} else if (!args.GetOrDefault(M::AgentLib).empty() || !args.GetOrDefault(M::AgentPath).empty()) {
LOG(WARNING) << "agent support has not been enabled. Enable experimental agent "
<< " support with '-XExperimental:agent'. Ignored options are:";
- for (auto op : args.GetOrDefault(M::AgentLib)) {
+ for (const auto& op : args.GetOrDefault(M::AgentLib)) {
if (op.HasArgs()) {
LOG(WARNING) << " -agentlib:" << op.GetName() << "=" << op.GetArgs();
} else {
LOG(WARNING) << " -agentlib:" << op.GetName();
}
}
- for (auto op : args.GetOrDefault(M::AgentPath)) {
+ for (const auto& op : args.GetOrDefault(M::AgentPath)) {
if (op.HasArgs()) {
LOG(WARNING) << " -agentpath:" << op.GetName() << "=" << op.GetArgs();
} else {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index e8f41d4..4e600ae 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1311,6 +1311,28 @@
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,
+// revisit this and make sure we're doing this on the right thread
+// (and we synchronize access to any shared data structures like "agents_")
+//
+void Runtime::AttachAgent(const std::string& agent_arg) {
+ ti::Agent agent(agent_arg);
+
+ int res = 0;
+ std::string err;
+ ti::Agent::LoadError result = agent.Attach(&res, &err);
+
+ if (result == ti::Agent::kNoError) {
+ agents_.push_back(std::move(agent));
+ } else {
+ LOG(ERROR) << "Agent attach failed (result=" << result << ") : " << err;
+ ScopedObjectAccess soa(Thread::Current());
+ ThrowWrappedIOException("%s", err.c_str());
+ }
+}
+
void Runtime::InitNativeMethods() {
VLOG(startup) << "Runtime::InitNativeMethods entering";
Thread* self = Thread::Current();
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 7cb87ab..b25ec23 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -665,6 +665,8 @@
NO_RETURN
static void Aborter(const char* abort_message);
+ void AttachAgent(const std::string& agent_arg);
+
private:
static void InitPlatformSignalHandlers();
diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc
index 7c0ea64..d21ff77 100644
--- a/runtime/ti/agent.cc
+++ b/runtime/ti/agent.cc
@@ -25,17 +25,10 @@
const char* AGENT_ON_ATTACH_FUNCTION_NAME = "Agent_OnAttach";
const char* AGENT_ON_UNLOAD_FUNCTION_NAME = "Agent_OnUnload";
-Agent Agent::Create(std::string arg) {
- size_t eq = arg.find_first_of('=');
- if (eq == std::string::npos) {
- return Agent(arg, "");
- } else {
- return Agent(arg.substr(0, eq), arg.substr(eq + 1, arg.length()));
- }
-}
-
// TODO We need to acquire some locks probably.
-Agent::LoadError Agent::Load(/*out*/jint* call_res, /*out*/ std::string* error_msg) {
+Agent::LoadError Agent::DoLoadHelper(bool attaching,
+ /*out*/jint* call_res,
+ /*out*/std::string* error_msg) {
DCHECK(call_res != nullptr);
DCHECK(error_msg != nullptr);
@@ -49,8 +42,10 @@
VLOG(agents) << "err: " << *error_msg;
return err;
}
- if (onload_ == nullptr) {
- *error_msg = StringPrintf("Unable to start agent %s: No Agent_OnLoad function found",
+ AgentOnLoadFunction callback = attaching ? onattach_ : onload_;
+ if (callback == nullptr) {
+ *error_msg = StringPrintf("Unable to start agent %s: No %s callback found",
+ (attaching ? "attach" : "load"),
name_.c_str());
VLOG(agents) << "err: " << *error_msg;
return kLoadingError;
@@ -59,9 +54,9 @@
std::unique_ptr<char[]> copied_args(new char[args_.size() + 1]);
strcpy(copied_args.get(), args_.c_str());
// TODO Need to do some checks that we are at a good spot etc.
- *call_res = onload_(static_cast<JavaVM*>(Runtime::Current()->GetJavaVM()),
- copied_args.get(),
- nullptr);
+ *call_res = callback(Runtime::Current()->GetJavaVM(),
+ copied_args.get(),
+ nullptr);
if (*call_res != 0) {
*error_msg = StringPrintf("Initialization of %s returned non-zero value of %d",
name_.c_str(), *call_res);
@@ -74,6 +69,12 @@
Agent::LoadError Agent::DoDlOpen(/*out*/std::string* error_msg) {
DCHECK(error_msg != nullptr);
+
+ DCHECK(dlopen_handle_ == nullptr);
+ DCHECK(onload_ == nullptr);
+ DCHECK(onattach_ == nullptr);
+ DCHECK(onunload_ == nullptr);
+
dlopen_handle_ = dlopen(name_.c_str(), RTLD_LAZY);
if (dlopen_handle_ == nullptr) {
*error_msg = StringPrintf("Unable to dlopen %s: %s", name_.c_str(), dlerror());
@@ -85,7 +86,7 @@
if (onload_ == nullptr) {
VLOG(agents) << "Unable to find 'Agent_OnLoad' symbol in " << this;
}
- onattach_ = reinterpret_cast<AgentOnAttachFunction>(dlsym(dlopen_handle_,
+ onattach_ = reinterpret_cast<AgentOnLoadFunction>(dlsym(dlopen_handle_,
AGENT_ON_ATTACH_FUNCTION_NAME));
if (onattach_ == nullptr) {
VLOG(agents) << "Unable to find 'Agent_OnAttach' symbol in " << this;
@@ -106,23 +107,93 @@
}
dlclose(dlopen_handle_);
dlopen_handle_ = nullptr;
+ onload_ = nullptr;
+ onattach_ = nullptr;
+ onunload_ = nullptr;
} else {
VLOG(agents) << this << " is not currently loaded!";
}
}
-Agent::Agent(const Agent& other)
- : name_(other.name_),
- args_(other.args_),
- dlopen_handle_(other.dlopen_handle_),
- onload_(other.onload_),
- onattach_(other.onattach_),
- onunload_(other.onunload_) {
- if (other.dlopen_handle_ != nullptr) {
- dlopen(other.name_.c_str(), 0);
+Agent::Agent(std::string arg)
+ : dlopen_handle_(nullptr),
+ onload_(nullptr),
+ onattach_(nullptr),
+ onunload_(nullptr) {
+ size_t eq = arg.find_first_of('=');
+ if (eq == std::string::npos) {
+ name_ = arg;
+ } else {
+ name_ = arg.substr(0, eq);
+ args_ = arg.substr(eq + 1, arg.length());
}
}
+Agent::Agent(const Agent& other)
+ : dlopen_handle_(nullptr),
+ onload_(nullptr),
+ onattach_(nullptr),
+ onunload_(nullptr) {
+ *this = other;
+}
+
+// Attempting to copy to/from loaded/started agents is a fatal error
+Agent& Agent::operator=(const Agent& other) {
+ if (this != &other) {
+ if (other.dlopen_handle_ != nullptr) {
+ LOG(FATAL) << "Attempting to copy a loaded agent!";
+ }
+
+ if (dlopen_handle_ != nullptr) {
+ LOG(FATAL) << "Attempting to assign into a loaded agent!";
+ }
+
+ DCHECK(other.onload_ == nullptr);
+ DCHECK(other.onattach_ == nullptr);
+ DCHECK(other.onunload_ == nullptr);
+
+ DCHECK(onload_ == nullptr);
+ DCHECK(onattach_ == nullptr);
+ DCHECK(onunload_ == nullptr);
+
+ name_ = other.name_;
+ args_ = other.args_;
+
+ dlopen_handle_ = nullptr;
+ onload_ = nullptr;
+ onattach_ = nullptr;
+ onunload_ = nullptr;
+ }
+ return *this;
+}
+
+Agent::Agent(Agent&& other)
+ : dlopen_handle_(nullptr),
+ onload_(nullptr),
+ onattach_(nullptr),
+ onunload_(nullptr) {
+ *this = std::move(other);
+}
+
+Agent& Agent::operator=(Agent&& other) {
+ if (this != &other) {
+ if (dlopen_handle_ != nullptr) {
+ dlclose(dlopen_handle_);
+ }
+ name_ = std::move(other.name_);
+ args_ = std::move(other.args_);
+ dlopen_handle_ = other.dlopen_handle_;
+ onload_ = other.onload_;
+ onattach_ = other.onattach_;
+ onunload_ = other.onunload_;
+ other.dlopen_handle_ = nullptr;
+ other.onload_ = nullptr;
+ other.onattach_ = nullptr;
+ other.onunload_ = nullptr;
+ }
+ return *this;
+}
+
Agent::~Agent() {
if (dlopen_handle_ != nullptr) {
dlclose(dlopen_handle_);
diff --git a/runtime/ti/agent.h b/runtime/ti/agent.h
index 521e21e..6561756 100644
--- a/runtime/ti/agent.h
+++ b/runtime/ti/agent.h
@@ -28,9 +28,10 @@
namespace ti {
using AgentOnLoadFunction = jint (*)(JavaVM*, const char*, void*);
-using AgentOnAttachFunction = jint (*)(JavaVM*, const char*, void*);
using AgentOnUnloadFunction = void (*)(JavaVM*);
+// TODO: consider splitting ti::Agent into command line, agent and shared library handler classes
+
class Agent {
public:
enum LoadError {
@@ -56,65 +57,44 @@
return !GetArgs().empty();
}
- // TODO We need to acquire some locks probably.
- LoadError Load(/*out*/jint* call_res, /*out*/std::string* error_msg);
+ LoadError Load(/*out*/jint* call_res, /*out*/std::string* error_msg) {
+ VLOG(agents) << "Loading agent: " << name_ << " " << args_;
+ return DoLoadHelper(false, call_res, error_msg);
+ }
// TODO We need to acquire some locks probably.
void Unload();
// Tries to attach the agent using its OnAttach method. Returns true on success.
- // TODO We need to acquire some locks probably.
- LoadError Attach(std::string* error_msg) {
- // TODO
- *error_msg = "Attach has not yet been implemented!";
- return kLoadingError;
+ LoadError Attach(/*out*/jint* call_res, /*out*/std::string* error_msg) {
+ VLOG(agents) << "Attaching agent: " << name_ << " " << args_;
+ return DoLoadHelper(true, call_res, error_msg);
}
- static Agent Create(std::string arg);
+ explicit Agent(std::string arg);
- static Agent Create(std::string name, std::string args) {
- return Agent(name, args);
- }
+ Agent(const Agent& other);
+ Agent& operator=(const Agent& other);
+
+ Agent(Agent&& other);
+ Agent& operator=(Agent&& other);
~Agent();
- // We need move constructor and copy for vectors
- Agent(const Agent& other);
-
- Agent(Agent&& other)
- : name_(other.name_),
- args_(other.args_),
- dlopen_handle_(nullptr),
- onload_(nullptr),
- onattach_(nullptr),
- onunload_(nullptr) {
- other.dlopen_handle_ = nullptr;
- other.onload_ = nullptr;
- other.onattach_ = nullptr;
- other.onunload_ = nullptr;
- }
-
- // We don't need an operator=
- void operator=(const Agent&) = delete;
-
private:
- Agent(std::string name, std::string args)
- : name_(name),
- args_(args),
- dlopen_handle_(nullptr),
- onload_(nullptr),
- onattach_(nullptr),
- onunload_(nullptr) { }
-
LoadError DoDlOpen(/*out*/std::string* error_msg);
- const std::string name_;
- const std::string args_;
+ LoadError DoLoadHelper(bool attaching,
+ /*out*/jint* call_res,
+ /*out*/std::string* error_msg);
+
+ std::string name_;
+ std::string args_;
void* dlopen_handle_;
// The entrypoints.
AgentOnLoadFunction onload_;
- AgentOnAttachFunction onattach_;
+ AgentOnLoadFunction onattach_;
AgentOnUnloadFunction onunload_;
friend std::ostream& operator<<(std::ostream &os, Agent const& m);
diff --git a/test/909-attach-agent/attach.cc b/test/909-attach-agent/attach.cc
new file mode 100644
index 0000000..2b50eb8
--- /dev/null
+++ b/test/909-attach-agent/attach.cc
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 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 "909-attach-agent/attach.h"
+
+#include <jni.h>
+#include <stdio.h>
+#include <string.h>
+#include "base/macros.h"
+#include "openjdkjvmti/jvmti.h"
+
+namespace art {
+namespace Test909AttachAgent {
+
+jint OnAttach(JavaVM* vm,
+ char* options ATTRIBUTE_UNUSED,
+ void* reserved ATTRIBUTE_UNUSED) {
+ printf("Attached Agent for test 909-attach-agent\n");
+ fsync(1);
+ jvmtiEnv* env = nullptr;
+ jvmtiEnv* env2 = nullptr;
+
+#define CHECK_CALL_SUCCESS(c) \
+ do { \
+ if ((c) != JNI_OK) { \
+ printf("call " #c " did not succeed\n"); \
+ return -1; \
+ } \
+ } while (false)
+
+ CHECK_CALL_SUCCESS(vm->GetEnv(reinterpret_cast<void**>(&env), JVMTI_VERSION_1_0));
+ CHECK_CALL_SUCCESS(vm->GetEnv(reinterpret_cast<void**>(&env2), JVMTI_VERSION_1_0));
+ if (env == env2) {
+ printf("GetEnv returned same environment twice!\n");
+ return -1;
+ }
+ unsigned char* local_data = nullptr;
+ CHECK_CALL_SUCCESS(env->Allocate(8, &local_data));
+ strcpy(reinterpret_cast<char*>(local_data), "hello!!");
+ CHECK_CALL_SUCCESS(env->SetEnvironmentLocalStorage(local_data));
+ unsigned char* get_data = nullptr;
+ CHECK_CALL_SUCCESS(env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&get_data)));
+ if (get_data != local_data) {
+ printf("Got different data from local storage then what was set!\n");
+ return -1;
+ }
+ CHECK_CALL_SUCCESS(env2->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&get_data)));
+ if (get_data != nullptr) {
+ printf("env2 did not have nullptr local storage.\n");
+ return -1;
+ }
+ CHECK_CALL_SUCCESS(env->Deallocate(local_data));
+ jint version = 0;
+ CHECK_CALL_SUCCESS(env->GetVersionNumber(&version));
+ if ((version & JVMTI_VERSION_1) != JVMTI_VERSION_1) {
+ printf("Unexpected version number!\n");
+ return -1;
+ }
+ CHECK_CALL_SUCCESS(env->DisposeEnvironment());
+ CHECK_CALL_SUCCESS(env2->DisposeEnvironment());
+#undef CHECK_CALL_SUCCESS
+ return JNI_OK;
+}
+
+} // namespace Test909AttachAgent
+} // namespace art
diff --git a/test/909-attach-agent/attach.h b/test/909-attach-agent/attach.h
new file mode 100644
index 0000000..3e6fe6c
--- /dev/null
+++ b/test/909-attach-agent/attach.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 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_TEST_909_ATTACH_AGENT_ATTACH_H_
+#define ART_TEST_909_ATTACH_AGENT_ATTACH_H_
+
+#include <jni.h>
+
+namespace art {
+namespace Test909AttachAgent {
+
+jint OnAttach(JavaVM* vm, char* options, void* reserved);
+
+} // namespace Test909AttachAgent
+} // namespace art
+
+#endif // ART_TEST_909_ATTACH_AGENT_ATTACH_H_
diff --git a/test/909-attach-agent/build b/test/909-attach-agent/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/909-attach-agent/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-build "$@" --experimental agents
diff --git a/test/909-attach-agent/expected.txt b/test/909-attach-agent/expected.txt
new file mode 100644
index 0000000..eacc595
--- /dev/null
+++ b/test/909-attach-agent/expected.txt
@@ -0,0 +1,3 @@
+Hello, world!
+Attached Agent for test 909-attach-agent
+Goodbye!
diff --git a/test/909-attach-agent/info.txt b/test/909-attach-agent/info.txt
new file mode 100644
index 0000000..06f3c8c
--- /dev/null
+++ b/test/909-attach-agent/info.txt
@@ -0,0 +1 @@
+Tests jvmti plugin attaching during live phase.
diff --git a/test/909-attach-agent/run b/test/909-attach-agent/run
new file mode 100755
index 0000000..aed6e83
--- /dev/null
+++ b/test/909-attach-agent/run
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+if [[ "$@" == *"-O"* ]]; then
+ agent=libtiagent.so
+ plugin=libopenjdkjvmti.so
+fi
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --android-runtime-option -Xplugin:${plugin} \
+ --args agent:${agent}=909-attach-agent
diff --git a/test/909-attach-agent/src/Main.java b/test/909-attach-agent/src/Main.java
new file mode 100644
index 0000000..8a8a087
--- /dev/null
+++ b/test/909-attach-agent/src/Main.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+import dalvik.system.VMDebug;
+import java.io.IOException;
+
+public class Main {
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ for(String a : args) {
+ if(a.startsWith("agent:")) {
+ String agent = a.substring(6);
+ try {
+ VMDebug.attachAgent(agent);
+ } catch(IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ System.out.println("Goodbye!");
+ }
+}
diff --git a/test/Android.bp b/test/Android.bp
index aaa1343..be1864c 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -252,6 +252,7 @@
"906-iterate-heap/iterate_heap.cc",
"907-get-loaded-classes/get_loaded_classes.cc",
"908-gc-start-finish/gc_callbacks.cc",
+ "909-attach-agent/attach.cc",
],
shared_libs: [
"libbase",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index d962472..9b456c1 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -277,6 +277,7 @@
906-iterate-heap \
907-get-loaded-classes \
908-gc-start-finish \
+ 909-attach-agent \
ifneq (,$(filter target,$(TARGET_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 79b41ec..90d0a66 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -32,6 +32,7 @@
#include "906-iterate-heap/iterate_heap.h"
#include "907-get-loaded-classes/get_loaded_classes.h"
#include "908-gc-start-finish/gc_callbacks.h"
+#include "909-attach-agent/attach.h"
namespace art {
@@ -56,6 +57,7 @@
{ "906-iterate-heap", Test906IterateHeap::OnLoad, nullptr },
{ "907-get-loaded-classes", Test907GetLoadedClasses::OnLoad, nullptr },
{ "908-gc-start-finish", Test908GcStartFinish::OnLoad, nullptr },
+ { "909-attach-agent", nullptr, Test909AttachAgent::OnAttach },
};
static AgentLib* FindAgent(char* name) {
@@ -105,7 +107,6 @@
return lib->load(vm, remaining_options, reserved);
}
-
extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
char* remaining_options = nullptr;
char* name_option = nullptr;