Create a basic agent loading framework.
Currently we only allow agents to be loaded at runtime startup, though
this is expected to change soon.
Test: ./test/run-test --host 900
Change-Id: Id648eaed4bbbe6fdef41d64922d023a4db0bfa54
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 1146f95..19c8db5 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -24,14 +24,15 @@
// Includes for the types that are being specialized
#include <string>
-#include "unit.h"
-#include "jdwp/jdwp.h"
#include "base/logging.h"
#include "base/time_utils.h"
#include "experimental_flags.h"
#include "gc/collector_type.h"
#include "gc/space/large_object_space.h"
+#include "jdwp/jdwp.h"
#include "jit/profile_saver_options.h"
+#include "ti/agent.h"
+#include "unit.h"
namespace art {
@@ -381,6 +382,22 @@
};
template <>
+struct CmdlineType<std::vector<ti::Agent>> : CmdlineTypeParser<std::vector<ti::Agent>> {
+ Result Parse(const std::string& args) {
+ assert(false && "Use AppendValues() for an Agent vector type");
+ return Result::Failure("Unconditional failure: Agent vector must be appended: " + args);
+ }
+
+ Result ParseAndAppend(const std::string& args,
+ std::vector<ti::Agent>& existing_value) {
+ existing_value.push_back(ti::Agent::Create(args));
+ return Result::SuccessNoValue();
+ }
+
+ static const char* Name() { return "std::vector<ti::Agent>"; }
+};
+
+template <>
struct CmdlineType<std::vector<std::string>> : CmdlineTypeParser<std::vector<std::string>> {
Result Parse(const std::string& args) {
assert(false && "Use AppendValues() for a string vector type");
@@ -625,6 +642,8 @@
log_verbosity.image = true;
} else if (verbose_options[j] == "systrace-locks") {
log_verbosity.systrace_lock_logging = true;
+ } else if (verbose_options[j] == "agents") {
+ log_verbosity.agents = true;
} else {
return Result::Usage(std::string("Unknown -verbose option ") + verbose_options[j]);
}
@@ -735,6 +754,8 @@
Result ParseAndAppend(const std::string& option, ExperimentalFlags& existing) {
if (option == "none") {
existing = ExperimentalFlags::kNone;
+ } else if (option == "agents") {
+ existing = existing | ExperimentalFlags::kAgents;
} else {
return Result::Failure(std::string("Unknown option '") + option + "'");
}
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 2f8b113..9c6ff5c 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -177,6 +177,7 @@
thread.cc \
thread_list.cc \
thread_pool.cc \
+ ti/agent.cc \
trace.cc \
transaction.cc \
type_lookup_table.cc \
@@ -370,6 +371,7 @@
stack.h \
thread.h \
thread_state.h \
+ ti/agent.h \
verifier/method_verifier.h
LIBOPENJDKJVM_SRC_FILES := openjdkjvm/OpenjdkJvm.cc
@@ -419,7 +421,7 @@
endif
ifneq ($(4),libart)
ifneq ($(4),libopenjdkjvm)
- $$(error expected libart of libopenjdkjvm for argument 4, received $(4))
+ $$(error expected libart or libopenjdkjvm for argument 4, received $(4))
endif
endif
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index 6323eee..ac21a3f 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -57,6 +57,7 @@
bool verifier;
bool image;
bool systrace_lock_logging; // Enabled with "-verbose:sys-locks".
+ bool agents;
};
// Global log verbosity setting, initialized by InitLogging.
diff --git a/runtime/experimental_flags.h b/runtime/experimental_flags.h
index fde1a5f..8e5a4ff 100644
--- a/runtime/experimental_flags.h
+++ b/runtime/experimental_flags.h
@@ -26,6 +26,7 @@
// The actual flag values.
enum {
kNone = 0x0000,
+ kAgents = 0x0001, // 0b00000001
};
constexpr ExperimentalFlags() : value_(0x0000) {}
@@ -61,9 +62,15 @@
uint32_t value_;
};
-inline std::ostream& operator<<(std::ostream& stream,
- const ExperimentalFlags& e ATTRIBUTE_UNUSED) {
- stream << "kNone";
+inline std::ostream& operator<<(std::ostream& stream, const ExperimentalFlags& e) {
+ bool started = false;
+ if (e & ExperimentalFlags::kAgents) {
+ stream << (started ? "|" : "") << "kAgents";
+ started = true;
+ }
+ if (!started) {
+ stream << "kNone";
+ }
return stream;
}
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index c7e4f8b..74cf752 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -23,6 +23,7 @@
#include "gc/heap.h"
#include "monitor.h"
#include "runtime.h"
+#include "ti/agent.h"
#include "trace.h"
#include "utils.h"
@@ -90,6 +91,13 @@
.Define({"-Xrunjdwp:_", "-agentlib:jdwp=_"})
.WithType<JDWP::JdwpOptions>()
.IntoKey(M::JdwpOptions)
+ // TODO Re-enable -agentlib: once I have a good way to transform the values.
+ // .Define("-agentlib:_")
+ // .WithType<std::vector<ti::Agent>>().AppendValues()
+ // .IntoKey(M::AgentLib)
+ .Define("-agentpath:_")
+ .WithType<std::vector<ti::Agent>>().AppendValues()
+ .IntoKey(M::AgentPath)
.Define("-Xms_")
.WithType<MemoryKiB>()
.IntoKey(M::MemoryInitialSize)
@@ -583,6 +591,30 @@
args.Set(M::HeapGrowthLimit, args.GetOrDefault(M::MemoryMaximumSize));
}
+ if (args.GetOrDefault(M::Experimental) & ExperimentalFlags::kAgents) {
+ LOG(WARNING) << "Experimental runtime agent support has been enabled. No guarantees are made "
+ << "the completeness, accuracy, reliability, or stability of the agent "
+ << "implementation. Use at your own risk. Do not attempt to write shipping code "
+ << "that relies on the implementation of any part of this api.";
+ } 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)) {
+ if (op.HasArgs()) {
+ LOG(WARNING) << " -agentlib:" << op.GetName() << "=" << op.GetArgs();
+ } else {
+ LOG(WARNING) << " -agentlib:" << op.GetName();
+ }
+ }
+ for (auto op : args.GetOrDefault(M::AgentPath)) {
+ if (op.HasArgs()) {
+ LOG(WARNING) << " -agentpath:" << op.GetName() << "=" << op.GetArgs();
+ } else {
+ LOG(WARNING) << " -agentpath:" << op.GetName();
+ }
+ }
+ }
+
*runtime_options = std::move(args);
return true;
}
@@ -627,6 +659,11 @@
UsageMessage(stream, " -showversion\n");
UsageMessage(stream, " -help\n");
UsageMessage(stream, " -agentlib:jdwp=options\n");
+ // TODO add back in once -agentlib actually does something.
+ // UsageMessage(stream, " -agentlib:library=options (Experimental feature, "
+ // "requires -Xexperimental:agent, some features might not be supported)\n");
+ UsageMessage(stream, " -agentpath:library_path=options (Experimental feature, "
+ "requires -Xexperimental:agent, some features might not be supported)\n");
UsageMessage(stream, "\n");
UsageMessage(stream, "The following extended options are supported:\n");
@@ -703,6 +740,8 @@
UsageMessage(stream, " -X[no]image-dex2oat (Whether to create and use a boot image)\n");
UsageMessage(stream, " -Xno-dex-file-fallback "
"(Don't fall back to dex files without oat files)\n");
+ UsageMessage(stream, " -Xexperimental:agents"
+ "(Enable new and experimental agent support)\n");
UsageMessage(stream, "\n");
UsageMessage(stream, "The following previously supported Dalvik options are ignored:\n");
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 68fa0d3..d84cdee 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -130,6 +130,7 @@
#include "signal_set.h"
#include "thread.h"
#include "thread_list.h"
+#include "ti/agent.h"
#include "trace.h"
#include "transaction.h"
#include "utils.h"
@@ -281,6 +282,11 @@
jit_->StopProfileSaver();
}
+ // TODO Maybe do some locking.
+ for (auto& agent : agents_) {
+ agent.Unload();
+ }
+
// Make sure our internal threads are dead before we start tearing down things they're using.
Dbg::StopJdwp();
delete signal_catcher_;
@@ -960,6 +966,13 @@
experimental_flags_ = runtime_options.GetOrDefault(Opt::Experimental);
is_low_memory_mode_ = runtime_options.Exists(Opt::LowMemoryMode);
+ if (experimental_flags_ & ExperimentalFlags::kAgents) {
+ agents_ = runtime_options.ReleaseOrDefault(Opt::AgentPath);
+ // TODO Add back in -agentlib
+ // for (auto lib : runtime_options.ReleaseOrDefault(Opt::AgentLib)) {
+ // agents_.push_back(lib);
+ // }
+ }
XGcOption xgc_option = runtime_options.GetOrDefault(Opt::GcOption);
heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize),
runtime_options.GetOrDefault(Opt::HeapGrowthLimit),
@@ -1232,6 +1245,20 @@
is_native_bridge_loaded_ = LoadNativeBridge(native_bridge_file_name);
}
+ // Startup agents
+ // TODO Maybe we should start a new thread to run these on. Investigate RI behavior more.
+ for (auto& agent : agents_) {
+ // TODO Check err
+ int res = 0;
+ std::string err = "";
+ ti::Agent::LoadError result = agent.Load(&res, &err);
+ if (result == ti::Agent::kInitializationError) {
+ LOG(FATAL) << "Unable to initialize agent!";
+ } else if (result != ti::Agent::kNoError) {
+ LOG(ERROR) << "Unable to load an agent: " << err;
+ }
+ }
+
VLOG(startup) << "Runtime::Init exiting";
return true;
diff --git a/runtime/runtime.h b/runtime/runtime.h
index c971646..10cc960 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -63,6 +63,9 @@
class String;
class Throwable;
} // namespace mirror
+namespace ti {
+ class Agent;
+} // namespace ti
namespace verifier {
class MethodVerifier;
enum class VerifyMode : int8_t;
@@ -698,6 +701,8 @@
std::string class_path_string_;
std::vector<std::string> properties_;
+ std::vector<ti::Agent> agents_;
+
// The default stack size for managed threads created by the runtime.
size_t default_stack_size_;
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index b95dfad..1409a4e 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -117,7 +117,9 @@
RUNTIME_OPTIONS_KEY (Unit, NoDexFileFallback)
RUNTIME_OPTIONS_KEY (std::string, CpuAbiList)
RUNTIME_OPTIONS_KEY (std::string, Fingerprint)
-RUNTIME_OPTIONS_KEY (ExperimentalFlags, Experimental, ExperimentalFlags::kNone) // -Xexperimental:{none}
+RUNTIME_OPTIONS_KEY (ExperimentalFlags, Experimental, ExperimentalFlags::kNone) // -Xexperimental:{none, agents}
+RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>, AgentLib) // -agentlib:<libname>=<options>, Requires -Xexperimental:agents
+RUNTIME_OPTIONS_KEY (std::vector<ti::Agent>, AgentPath) // -agentpath:<libname>=<options>, Requires -Xexperimental:agents
// Not parse-able from command line, but can be provided explicitly.
// (Do not add anything here that is defined in ParsedOptions::MakeParser)
diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc
new file mode 100644
index 0000000..41a21f7
--- /dev/null
+++ b/runtime/ti/agent.cc
@@ -0,0 +1,138 @@
+/*
+ * 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 "agent.h"
+#include "java_vm_ext.h"
+#include "runtime.h"
+
+namespace art {
+namespace ti {
+
+const char* AGENT_ON_LOAD_FUNCTION_NAME = "Agent_OnLoad";
+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) {
+ DCHECK(call_res != nullptr);
+ DCHECK(error_msg != nullptr);
+ if (IsStarted()) {
+ *error_msg = StringPrintf("the agent at %s has already been started!", name_.c_str());
+ VLOG(agents) << "err: " << *error_msg;
+ return kAlreadyStarted;
+ }
+ LoadError err = DoDlOpen(error_msg);
+ if (err != kNoError) {
+ VLOG(agents) << "err: " << *error_msg;
+ return err;
+ }
+ if (onload_ == nullptr) {
+ *error_msg = StringPrintf("Unable to start agent %s: No Agent_OnLoad function found",
+ name_.c_str());
+ VLOG(agents) << "err: " << *error_msg;
+ return kLoadingError;
+ }
+ // TODO Need to do some checks that we are at a good spot etc.
+ *call_res = onload_(static_cast<JavaVM*>(Runtime::Current()->GetJavaVM()),
+ args_.c_str(),
+ nullptr);
+ if (*call_res != 0) {
+ *error_msg = StringPrintf("Initialization of %s returned non-zero value of %d",
+ name_.c_str(), *call_res);
+ VLOG(agents) << "err: " << *error_msg;
+ return kInitializationError;
+ } else {
+ return kNoError;
+ }
+}
+
+Agent::LoadError Agent::DoDlOpen(/*out*/std::string* error_msg) {
+ DCHECK(error_msg != 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());
+ return kLoadingError;
+ }
+
+ onload_ = reinterpret_cast<AgentOnLoadFunction>(dlsym(dlopen_handle_,
+ AGENT_ON_LOAD_FUNCTION_NAME));
+ if (onload_ == nullptr) {
+ VLOG(agents) << "Unable to find 'Agent_OnLoad' symbol in " << this;
+ }
+ onattach_ = reinterpret_cast<AgentOnAttachFunction>(dlsym(dlopen_handle_,
+ AGENT_ON_ATTACH_FUNCTION_NAME));
+ if (onattach_ == nullptr) {
+ VLOG(agents) << "Unable to find 'Agent_OnAttach' symbol in " << this;
+ }
+ onunload_= reinterpret_cast<AgentOnUnloadFunction>(dlsym(dlopen_handle_,
+ AGENT_ON_UNLOAD_FUNCTION_NAME));
+ if (onunload_ == nullptr) {
+ VLOG(agents) << "Unable to find 'Agent_OnUnload' symbol in " << this;
+ }
+ return kNoError;
+}
+
+// TODO Lock some stuff probably.
+void Agent::Unload() {
+ if (dlopen_handle_ != nullptr) {
+ if (onunload_ != nullptr) {
+ onunload_(Runtime::Current()->GetJavaVM());
+ }
+ dlclose(dlopen_handle_);
+ dlopen_handle_ = 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() {
+ if (dlopen_handle_ != nullptr) {
+ dlclose(dlopen_handle_);
+ }
+}
+
+std::ostream& operator<<(std::ostream &os, const Agent* m) {
+ return os << *m;
+}
+
+std::ostream& operator<<(std::ostream &os, Agent const& m) {
+ return os << "Agent { name=\"" << m.name_ << "\", args=\"" << m.args_ << "\", handle="
+ << m.dlopen_handle_ << " }";
+}
+
+} // namespace ti
+} // namespace art
diff --git a/runtime/ti/agent.h b/runtime/ti/agent.h
new file mode 100644
index 0000000..521e21e
--- /dev/null
+++ b/runtime/ti/agent.h
@@ -0,0 +1,130 @@
+/*
+ * 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_RUNTIME_TI_AGENT_H_
+#define ART_RUNTIME_TI_AGENT_H_
+
+#include <dlfcn.h>
+#include <jni.h> // for jint, JavaVM* etc declarations
+
+#include "base/stringprintf.h"
+#include "runtime.h"
+#include "utils.h"
+
+namespace art {
+namespace ti {
+
+using AgentOnLoadFunction = jint (*)(JavaVM*, const char*, void*);
+using AgentOnAttachFunction = jint (*)(JavaVM*, const char*, void*);
+using AgentOnUnloadFunction = void (*)(JavaVM*);
+
+class Agent {
+ public:
+ enum LoadError {
+ kNoError, // No error occurred..
+ kAlreadyStarted, // The agent has already been loaded.
+ kLoadingError, // dlopen or dlsym returned an error.
+ kInitializationError, // The entrypoint did not return 0. This might require an abort.
+ };
+
+ bool IsStarted() const {
+ return dlopen_handle_ != nullptr;
+ }
+
+ const std::string& GetName() const {
+ return name_;
+ }
+
+ const std::string& GetArgs() const {
+ return args_;
+ }
+
+ bool HasArgs() const {
+ return !GetArgs().empty();
+ }
+
+ // TODO We need to acquire some locks probably.
+ LoadError Load(/*out*/jint* call_res, /*out*/std::string* 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;
+ }
+
+ static Agent Create(std::string arg);
+
+ static Agent Create(std::string name, std::string args) {
+ return Agent(name, args);
+ }
+
+ ~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_;
+ void* dlopen_handle_;
+
+ // The entrypoints.
+ AgentOnLoadFunction onload_;
+ AgentOnAttachFunction onattach_;
+ AgentOnUnloadFunction onunload_;
+
+ friend std::ostream& operator<<(std::ostream &os, Agent const& m);
+};
+
+std::ostream& operator<<(std::ostream &os, Agent const& m);
+std::ostream& operator<<(std::ostream &os, const Agent* m);
+
+} // namespace ti
+} // namespace art
+
+#endif // ART_RUNTIME_TI_AGENT_H_
+
diff --git a/test/900-hello-plugin/build b/test/900-hello-plugin/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/900-hello-plugin/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/900-hello-plugin/expected.txt b/test/900-hello-plugin/expected.txt
new file mode 100644
index 0000000..d31eead
--- /dev/null
+++ b/test/900-hello-plugin/expected.txt
@@ -0,0 +1,3 @@
+Agent_OnLoad called with options "test_900"
+Hello, world!
+Agent_OnUnload called
diff --git a/test/900-hello-plugin/info.txt b/test/900-hello-plugin/info.txt
new file mode 100644
index 0000000..04bb3c8
--- /dev/null
+++ b/test/900-hello-plugin/info.txt
@@ -0,0 +1,2 @@
+Test that agents are loaded.
+
diff --git a/test/900-hello-plugin/load_unload.cc b/test/900-hello-plugin/load_unload.cc
new file mode 100644
index 0000000..3315a86
--- /dev/null
+++ b/test/900-hello-plugin/load_unload.cc
@@ -0,0 +1,37 @@
+/*
+ * 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 <jni.h>
+#include <stdio.h>
+
+#include "art_method-inl.h"
+#include "base/logging.h"
+#include "base/macros.h"
+
+namespace art {
+
+extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm ATTRIBUTE_UNUSED,
+ char* options,
+ void* reserved ATTRIBUTE_UNUSED) {
+ printf("Agent_OnLoad called with options \"%s\"\n", options);
+ return 0;
+}
+
+extern "C" JNIEXPORT void JNICALL Agent_OnUnload(JavaVM* vm ATTRIBUTE_UNUSED) {
+ printf("Agent_OnUnload called\n");
+}
+
+} // namespace art
diff --git a/test/900-hello-plugin/run b/test/900-hello-plugin/run
new file mode 100755
index 0000000..c533422
--- /dev/null
+++ b/test/900-hello-plugin/run
@@ -0,0 +1,18 @@
+#!/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-run "$@" --experimental agents \
+ --runtime-option -agentpath:libartagentd.so=test_900
diff --git a/test/900-hello-plugin/src/Main.java b/test/900-hello-plugin/src/Main.java
new file mode 100644
index 0000000..1ef6289
--- /dev/null
+++ b/test/900-hello-plugin/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ }
+}
diff --git a/test/Android.libartagent.mk b/test/Android.libartagent.mk
new file mode 100644
index 0000000..729de3f
--- /dev/null
+++ b/test/Android.libartagent.mk
@@ -0,0 +1,101 @@
+#
+# 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.
+#
+
+
+LOCAL_PATH := $(call my-dir)
+
+include art/build/Android.common_build.mk
+
+LIBARTAGENT_COMMON_SRC_FILES := \
+ 900-hello-plugin/load_unload.cc
+
+# $(1): target or host
+# $(2): debug or <empty>
+define build-libartagent
+ ifneq ($(1),target)
+ ifneq ($(1),host)
+ $$(error expected target or host for argument 1, received $(1))
+ endif
+ endif
+ ifneq ($(2),debug)
+ ifneq ($(2),)
+ $$(error d or empty for argument 2, received $(2))
+ endif
+ suffix := d
+ else
+ suffix :=
+ endif
+
+ art_target_or_host := $(1)
+
+ include $(CLEAR_VARS)
+ LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
+ LOCAL_MODULE := libartagent$$(suffix)
+ ifeq ($$(art_target_or_host),target)
+ LOCAL_MODULE_TAGS := tests
+ endif
+ LOCAL_SRC_FILES := $(LIBARTAGENT_COMMON_SRC_FILES)
+ LOCAL_SHARED_LIBRARIES += libart$$(suffix) libbacktrace libnativehelper
+ LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
+ LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
+ LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.libartagent.mk
+ ifeq ($$(art_target_or_host),target)
+ $(call set-target-local-clang-vars)
+ ifeq ($$(suffix),d)
+ $(call set-target-local-cflags-vars,debug)
+ else
+ $(call set-target-local-cflags-vars,ndebug)
+ endif
+ LOCAL_SHARED_LIBRARIES += libdl
+ LOCAL_MULTILIB := both
+ LOCAL_MODULE_PATH_32 := $(ART_TARGET_TEST_OUT)/$(ART_TARGET_ARCH_32)
+ LOCAL_MODULE_PATH_64 := $(ART_TARGET_TEST_OUT)/$(ART_TARGET_ARCH_64)
+ LOCAL_MODULE_TARGET_ARCH := $(ART_SUPPORTED_ARCH)
+ include $(BUILD_SHARED_LIBRARY)
+ else # host
+ LOCAL_CLANG := $(ART_HOST_CLANG)
+ LOCAL_CFLAGS := $(ART_HOST_CFLAGS)
+ LOCAL_ASFLAGS := $(ART_HOST_ASFLAGS)
+ ifeq ($$(suffix),d)
+ LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS)
+ LOCAL_ASFLAGS += $(ART_HOST_DEBUG_ASFLAGS)
+ else
+ LOCAL_CFLAGS += $(ART_HOST_NON_DEBUG_CFLAGS)
+ LOCAL_ASFLAGS += $(ART_HOST_NON_DEBUG_ASFLAGS)
+ endif
+ LOCAL_LDLIBS := $(ART_HOST_LDLIBS) -ldl -lpthread
+ LOCAL_IS_HOST_MODULE := true
+ LOCAL_MULTILIB := both
+ include $(BUILD_HOST_SHARED_LIBRARY)
+ endif
+
+ # Clear locally used variables.
+ art_target_or_host :=
+ suffix :=
+endef
+
+ifeq ($(ART_BUILD_TARGET),true)
+ $(eval $(call build-libartagent,target,))
+ $(eval $(call build-libartagent,target,debug))
+endif
+ifeq ($(ART_BUILD_HOST),true)
+ $(eval $(call build-libartagent,host,))
+ $(eval $(call build-libartagent,host,debug))
+endif
+
+# Clear locally used variables.
+LOCAL_PATH :=
+LIBARTAGENT_COMMON_SRC_FILES :=
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index bba6f8e..05ac5f4 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -653,6 +653,14 @@
# only once).
TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUTS)
+# Also need libartagent.
+TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libartagent.so
+TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libartagentd.so
+ifdef TARGET_2ND_ARCH
+TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_2ND_ARCH)/libartagent.so
+TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_2ND_ARCH)/libartagentd.so
+endif
+
# Also need libarttest.
TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttestd.so
@@ -671,6 +679,8 @@
# specific version depending on the compiler.
ART_TEST_HOST_RUN_TEST_DEPENDENCIES := \
$(ART_HOST_EXECUTABLES) \
+ $(ART_HOST_OUT_SHARED_LIBRARIES)/libartagent$(ART_HOST_SHLIB_EXTENSION) \
+ $(ART_HOST_OUT_SHARED_LIBRARIES)/libartagentd$(ART_HOST_SHLIB_EXTENSION) \
$(ART_HOST_OUT_SHARED_LIBRARIES)/libarttest$(ART_HOST_SHLIB_EXTENSION) \
$(ART_HOST_OUT_SHARED_LIBRARIES)/libarttestd$(ART_HOST_SHLIB_EXTENSION) \
$(ART_HOST_OUT_SHARED_LIBRARIES)/libnativebridgetest$(ART_HOST_SHLIB_EXTENSION) \
@@ -680,6 +690,8 @@
ifneq ($(HOST_PREFER_32_BIT),true)
ART_TEST_HOST_RUN_TEST_DEPENDENCIES += \
+ $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libartagent$(ART_HOST_SHLIB_EXTENSION) \
+ $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libartagentd$(ART_HOST_SHLIB_EXTENSION) \
$(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libarttest$(ART_HOST_SHLIB_EXTENSION) \
$(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libarttestd$(ART_HOST_SHLIB_EXTENSION) \
$(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libnativebridgetest$(ART_HOST_SHLIB_EXTENSION) \
@@ -1092,5 +1104,9 @@
RUN_TYPES :=
DEBUGGABLE_TYPES :=
-include $(LOCAL_PATH)/Android.libarttest.mk
-include art/test/Android.libnativebridgetest.mk
+MY_LOCAL_PATH := $(LOCAL_PATH)
+include $(MY_LOCAL_PATH)/Android.libartagent.mk
+include $(MY_LOCAL_PATH)/Android.libarttest.mk
+include $(MY_LOCAL_PATH)/Android.libnativebridgetest.mk
+MY_LOCAL_PATH :=
+LOCAL_PATH :=
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index c6c9380..0ea5d52 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -21,6 +21,7 @@
EXPERIMENTAL=""
FALSE_BIN="/system/bin/false"
FLAGS=""
+ANDROID_FLAGS=""
GDB=""
GDB_ARGS=""
GDB_SERVER="gdbserver"
@@ -93,6 +94,11 @@
FLAGS="${FLAGS} -Xcompiler-option $option"
COMPILE_FLAGS="${COMPILE_FLAGS} $option"
shift
+ elif [ "x$1" = "x--android-runtime-option" ]; then
+ shift
+ option="$1"
+ ANDROID_FLAGS="${ANDROID_FLAGS} $option"
+ shift
elif [ "x$1" = "x--runtime-option" ]; then
shift
option="$1"
@@ -233,6 +239,7 @@
done
if [ "$USE_JVM" = "n" ]; then
+ FLAGS="${FLAGS} ${ANDROID_FLAGS}"
for feature in ${EXPERIMENTAL}; do
FLAGS="${FLAGS} -Xexperimental:${feature} -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:${feature}"
COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xexperimental:${feature}"
@@ -469,12 +476,12 @@
adb push $TEST_NAME-ex.jar $DEX_LOCATION >/dev/null 2>&1
fi
- LD_LIBRARY_PATH=
+ LD_LIBRARY_PATH=/data/art-test/$ISA
if [ "$ANDROID_ROOT" != "/system" ]; then
# Current default installation is dalvikvm 64bits and dex2oat 32bits,
# so we can only use LD_LIBRARY_PATH when testing on a local
# installation.
- LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBRARY_DIRECTORY
+ LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBRARY_DIRECTORY:$LD_LIBRARY_PATH
fi
PUBLIC_LIBS=libart.so:libartd.so