OSDN Git Service

Create a basic agent loading framework.
authorAlex Light <allight@google.com>
Thu, 28 Jul 2016 17:07:45 +0000 (10:07 -0700)
committerAlex Light <allight@google.com>
Thu, 11 Aug 2016 20:48:13 +0000 (13:48 -0700)
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

19 files changed:
cmdline/cmdline_types.h
runtime/Android.mk
runtime/base/logging.h
runtime/experimental_flags.h
runtime/parsed_options.cc
runtime/runtime.cc
runtime/runtime.h
runtime/runtime_options.def
runtime/ti/agent.cc [new file with mode: 0644]
runtime/ti/agent.h [new file with mode: 0644]
test/900-hello-plugin/build [new file with mode: 0755]
test/900-hello-plugin/expected.txt [new file with mode: 0644]
test/900-hello-plugin/info.txt [new file with mode: 0644]
test/900-hello-plugin/load_unload.cc [new file with mode: 0644]
test/900-hello-plugin/run [new file with mode: 0755]
test/900-hello-plugin/src/Main.java [new file with mode: 0644]
test/Android.libartagent.mk [new file with mode: 0644]
test/Android.run-test.mk
test/etc/run-test-jar

index 1146f95..19c8db5 100644 (file)
 
 // 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 @@ struct CmdlineType<std::string> : CmdlineTypeParser<std::string> {
 };
 
 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 @@ struct CmdlineType<LogVerbosity> : CmdlineTypeParser<LogVerbosity> {
         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 @@ struct CmdlineType<ExperimentalFlags> : CmdlineTypeParser<ExperimentalFlags> {
   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 + "'");
     }
index 2f8b113..9c6ff5c 100644 (file)
@@ -177,6 +177,7 @@ LIBART_COMMON_SRC_FILES := \
   thread.cc \
   thread_list.cc \
   thread_pool.cc \
+  ti/agent.cc \
   trace.cc \
   transaction.cc \
   type_lookup_table.cc \
@@ -370,6 +371,7 @@ LIBART_ENUM_OPERATOR_OUT_HEADER_FILES := \
   stack.h \
   thread.h \
   thread_state.h \
+  ti/agent.h \
   verifier/method_verifier.h
 
 LIBOPENJDKJVM_SRC_FILES := openjdkjvm/OpenjdkJvm.cc
@@ -419,7 +421,7 @@ define build-runtime-library
   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
 
index 6323eee..ac21a3f 100644 (file)
@@ -57,6 +57,7 @@ struct LogVerbosity {
   bool verifier;
   bool image;
   bool systrace_lock_logging;  // Enabled with "-verbose:sys-locks".
+  bool agents;
 };
 
 // Global log verbosity setting, initialized by InitLogging.
index fde1a5f..8e5a4ff 100644 (file)
@@ -26,6 +26,7 @@ struct ExperimentalFlags {
   // The actual flag values.
   enum {
     kNone           = 0x0000,
+    kAgents         = 0x0001,  // 0b00000001
   };
 
   constexpr ExperimentalFlags() : value_(0x0000) {}
@@ -61,9 +62,15 @@ struct ExperimentalFlags {
   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;
 }
 
index c7e4f8b..74cf752 100644 (file)
@@ -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 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize
       .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 @@ bool ParsedOptions::DoParse(const RuntimeOptions& options,
     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 @@ void ParsedOptions::Usage(const char* fmt, ...) {
   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 @@ void ParsedOptions::Usage(const char* fmt, ...) {
   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");
index 68fa0d3..d84cdee 100644 (file)
 #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 @@ Runtime::~Runtime() {
     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 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
   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 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
     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;
index c971646..10cc960 100644 (file)
@@ -63,6 +63,9 @@ namespace mirror {
   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 @@ class Runtime {
   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_;
 
index b95dfad..1409a4e 100644 (file)
@@ -117,7 +117,9 @@ RUNTIME_OPTIONS_KEY (unsigned int,        ZygoteMaxFailedBoots,           10)
 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 (file)
index 0000000..41a21f7
--- /dev/null
@@ -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 (file)
index 0000000..521e21e
--- /dev/null
@@ -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 (executable)
index 0000000..898e2e5
--- /dev/null
@@ -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 (file)
index 0000000..d31eead
--- /dev/null
@@ -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 (file)
index 0000000..04bb3c8
--- /dev/null
@@ -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 (file)
index 0000000..3315a86
--- /dev/null
@@ -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 (executable)
index 0000000..c533422
--- /dev/null
@@ -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 (file)
index 0000000..1ef6289
--- /dev/null
@@ -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 (file)
index 0000000..729de3f
--- /dev/null
@@ -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 :=
index bba6f8e..05ac5f4 100644 (file)
@@ -653,6 +653,14 @@ $(foreach target, $(TARGET_TYPES), \
 # 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 @@ endif
 # 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 @@ ART_TEST_HOST_RUN_TEST_DEPENDENCIES := \
 
 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 @@ ALL_ADDRESS_SIZES :=
 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 :=
index c6c9380..0ea5d52 100755 (executable)
@@ -21,6 +21,7 @@ DEX2OAT=""
 EXPERIMENTAL=""
 FALSE_BIN="/system/bin/false"
 FLAGS=""
+ANDROID_FLAGS=""
 GDB=""
 GDB_ARGS=""
 GDB_SERVER="gdbserver"
@@ -93,6 +94,11 @@ while true; do
         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 @@ while true; do
 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 @@ if [ "$HOST" = "n" ]; then
       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