diff options
-rw-r--r-- | adbconnection/adbconnection.cc | 4 | ||||
-rw-r--r-- | runtime/native/dalvik_system_VMDebug.cc | 6 | ||||
-rw-r--r-- | runtime/runtime.cc | 4 | ||||
-rw-r--r-- | runtime/runtime.h | 2 | ||||
-rw-r--r-- | runtime/ti/agent.cc | 47 | ||||
-rw-r--r-- | runtime/ti/agent.h | 15 | ||||
-rw-r--r-- | test/909-attach-agent/expected.txt | 7 | ||||
-rw-r--r-- | test/909-attach-agent/src-art/Main.java | 74 |
8 files changed, 135 insertions, 24 deletions
diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc index 2a9982a6e4..a5c885a933 100644 --- a/adbconnection/adbconnection.cc +++ b/adbconnection/adbconnection.cc @@ -583,7 +583,9 @@ void AdbConnectionState::RunPollLoop(art::Thread* self) { DCHECK(!agent_listening_); // Load the agent now! self->AssertNoPendingException(); - art::Runtime::Current()->AttachAgent(MakeAgentArg()); + art::Runtime::Current()->AttachAgent(/* JNIEnv* */ nullptr, + MakeAgentArg(), + /* classloader */ nullptr); if (self->IsExceptionPending()) { LOG(ERROR) << "Failed to load agent " << agent_name_; art::ScopedObjectAccess soa(self); diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index 787646dd21..6da34bcc60 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -548,7 +548,7 @@ static jobjectArray VMDebug_getRuntimeStatsInternal(JNIEnv* env, jclass) { return result; } -static void VMDebug_attachAgent(JNIEnv* env, jclass, jstring agent) { +static void VMDebug_nativeAttachAgent(JNIEnv* env, jclass, jstring agent, jobject classloader) { if (agent == nullptr) { ScopedObjectAccess soa(env); ThrowNullPointerException("agent is null"); @@ -570,7 +570,7 @@ static void VMDebug_attachAgent(JNIEnv* env, jclass, jstring agent) { filename = chars.c_str(); } - Runtime::Current()->AttachAgent(filename); + Runtime::Current()->AttachAgent(env, filename, classloader); } static JNINativeMethod gMethods[] = { @@ -607,7 +607,7 @@ static JNINativeMethod gMethods[] = { FAST_NATIVE_METHOD(VMDebug, threadCpuTimeNanos, "()J"), NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"), NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;"), - NATIVE_METHOD(VMDebug, attachAgent, "(Ljava/lang/String;)V"), + NATIVE_METHOD(VMDebug, nativeAttachAgent, "(Ljava/lang/String;Ljava/lang/ClassLoader;)V"), }; void register_dalvik_system_VMDebug(JNIEnv* env) { diff --git a/runtime/runtime.cc b/runtime/runtime.cc index e1610ab533..c61d7ef6ec 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1575,7 +1575,7 @@ static bool EnsureJvmtiPlugin(Runtime* runtime, // 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) { +void Runtime::AttachAgent(JNIEnv* env, const std::string& agent_arg, jobject class_loader) { std::string error_msg; if (!EnsureJvmtiPlugin(this, &plugins_, &error_msg)) { LOG(WARNING) << "Could not load plugin: " << error_msg; @@ -1588,7 +1588,7 @@ void Runtime::AttachAgent(const std::string& agent_arg) { int res = 0; ti::LoadError error; - std::unique_ptr<ti::Agent> agent = agent_spec.Attach(&res, &error, &error_msg); + std::unique_ptr<ti::Agent> agent = agent_spec.Attach(env, class_loader, &res, &error, &error_msg); if (agent != nullptr) { agents_.push_back(std::move(agent)); diff --git a/runtime/runtime.h b/runtime/runtime.h index ac29ed42c3..4da2cd495b 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -661,7 +661,7 @@ class Runtime { void AddSystemWeakHolder(gc::AbstractSystemWeakHolder* holder); void RemoveSystemWeakHolder(gc::AbstractSystemWeakHolder* holder); - void AttachAgent(const std::string& agent_arg); + void AttachAgent(JNIEnv* env, const std::string& agent_arg, jobject class_loader); const std::list<std::unique_ptr<ti::Agent>>& GetAgents() const { return agents_; diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc index aa09d84d74..62bdde6790 100644 --- a/runtime/ti/agent.cc +++ b/runtime/ti/agent.cc @@ -17,6 +17,8 @@ #include "agent.h" #include "android-base/stringprintf.h" +#include "nativehelper/scoped_local_ref.h" +#include "nativeloader/native_loader.h" #include "base/strlcpy.h" #include "java_vm_ext.h" @@ -47,20 +49,24 @@ std::unique_ptr<Agent> AgentSpec::Load(/*out*/jint* call_res, /*out*/LoadError* error, /*out*/std::string* error_msg) { VLOG(agents) << "Loading agent: " << name_ << " " << args_; - return DoLoadHelper(false, call_res, error, error_msg); + return DoLoadHelper(nullptr, false, nullptr, call_res, error, error_msg); } // Tries to attach the agent using its OnAttach method. Returns true on success. -std::unique_ptr<Agent> AgentSpec::Attach(/*out*/jint* call_res, +std::unique_ptr<Agent> AgentSpec::Attach(JNIEnv* env, + jobject class_loader, + /*out*/jint* call_res, /*out*/LoadError* error, /*out*/std::string* error_msg) { VLOG(agents) << "Attaching agent: " << name_ << " " << args_; - return DoLoadHelper(true, call_res, error, error_msg); + return DoLoadHelper(env, true, class_loader, call_res, error, error_msg); } // TODO We need to acquire some locks probably. -std::unique_ptr<Agent> AgentSpec::DoLoadHelper(bool attaching, +std::unique_ptr<Agent> AgentSpec::DoLoadHelper(JNIEnv* env, + bool attaching, + jobject class_loader, /*out*/jint* call_res, /*out*/LoadError* error, /*out*/std::string* error_msg) { @@ -68,7 +74,7 @@ std::unique_ptr<Agent> AgentSpec::DoLoadHelper(bool attaching, DCHECK(call_res != nullptr); DCHECK(error_msg != nullptr); - std::unique_ptr<Agent> agent = DoDlOpen(error, error_msg); + std::unique_ptr<Agent> agent = DoDlOpen(env, class_loader, error, error_msg); if (agent == nullptr) { VLOG(agents) << "err: " << *error_msg; return nullptr; @@ -99,15 +105,37 @@ std::unique_ptr<Agent> AgentSpec::DoLoadHelper(bool attaching, return agent; } -std::unique_ptr<Agent> AgentSpec::DoDlOpen(/*out*/LoadError* error, /*out*/std::string* error_msg) { +std::unique_ptr<Agent> AgentSpec::DoDlOpen(JNIEnv* env, + jobject class_loader, + /*out*/LoadError* error, + /*out*/std::string* error_msg) { DCHECK(error_msg != nullptr); - void* dlopen_handle = dlopen(name_.c_str(), RTLD_LAZY); + ScopedLocalRef<jstring> library_path(env, + class_loader == nullptr + ? nullptr + : JavaVMExt::GetLibrarySearchPath(env, class_loader)); + + bool needs_native_bridge = false; + void* dlopen_handle = android::OpenNativeLibrary(env, + Runtime::Current()->GetTargetSdkVersion(), + name_.c_str(), + class_loader, + library_path.get(), + &needs_native_bridge, + error_msg); if (dlopen_handle == nullptr) { *error_msg = StringPrintf("Unable to dlopen %s: %s", name_.c_str(), dlerror()); *error = kLoadingError; return nullptr; } + if (needs_native_bridge) { + // TODO: Consider support? + android::CloseNativeLibrary(dlopen_handle, needs_native_bridge); + *error_msg = StringPrintf("Native-bridge agents unsupported: %s", name_.c_str()); + *error = kLoadingError; + return nullptr; + } std::unique_ptr<Agent> agent(new Agent(name_, dlopen_handle)); agent->PopulateFunctions(); @@ -131,8 +159,9 @@ void Agent::Unload() { if (onunload_ != nullptr) { onunload_(Runtime::Current()->GetJavaVM()); } - // Don't actually dlclose since some agents assume they will never get unloaded. Since this only - // happens when the runtime is shutting down anyway this isn't a big deal. + // Don't actually android::CloseNativeLibrary since some agents assume they will never get + // unloaded. Since this only happens when the runtime is shutting down anyway this isn't a big + // deal. dlopen_handle_ = nullptr; onload_ = nullptr; onattach_ = nullptr; diff --git a/runtime/ti/agent.h b/runtime/ti/agent.h index 68b49485be..24a6f1ce6a 100644 --- a/runtime/ti/agent.h +++ b/runtime/ti/agent.h @@ -56,14 +56,21 @@ class AgentSpec { /*out*/std::string* error_msg); // Tries to attach the agent using its OnAttach method. Returns true on success. - std::unique_ptr<Agent> Attach(/*out*/jint* call_res, + std::unique_ptr<Agent> Attach(JNIEnv* env, + jobject class_loader, + /*out*/jint* call_res, /*out*/LoadError* error, /*out*/std::string* error_msg); private: - std::unique_ptr<Agent> DoDlOpen(/*out*/LoadError* error, /*out*/std::string* error_msg); - - std::unique_ptr<Agent> DoLoadHelper(bool attaching, + std::unique_ptr<Agent> DoDlOpen(JNIEnv* env, + jobject class_loader, + /*out*/LoadError* error, + /*out*/std::string* error_msg); + + std::unique_ptr<Agent> DoLoadHelper(JNIEnv* env, + bool attaching, + jobject class_loader, /*out*/jint* call_res, /*out*/LoadError* error, /*out*/std::string* error_msg); diff --git a/test/909-attach-agent/expected.txt b/test/909-attach-agent/expected.txt index c0bccd6486..4d687f531e 100644 --- a/test/909-attach-agent/expected.txt +++ b/test/909-attach-agent/expected.txt @@ -1,11 +1,12 @@ Hello, world! Attached Agent for test 909-attach-agent +Attached Agent for test 909-attach-agent Goodbye! Hello, world! Attached Agent for test 909-attach-agent +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) +Process is not debuggable. +Process is not debuggable. Goodbye! diff --git a/test/909-attach-agent/src-art/Main.java b/test/909-attach-agent/src-art/Main.java index 25ebd57236..705e61eb99 100644 --- a/test/909-attach-agent/src-art/Main.java +++ b/test/909-attach-agent/src-art/Main.java @@ -14,7 +14,13 @@ * limitations under the License. */ +import dalvik.system.PathClassLoader; import dalvik.system.VMDebug; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.File; import java.io.IOException; public class Main { @@ -26,10 +32,76 @@ public class Main { try { VMDebug.attachAgent(agent); } catch(IOException e) { - e.printStackTrace(System.out); + System.out.println(e.getMessage()); } } } + attachWithClassLoader(args); System.out.println("Goodbye!"); } + + private static void attachWithClassLoader(String[] args) { + for(String a : args) { + if(a.startsWith("agent:")) { + String agentName = a.substring(6, a.indexOf('=')); + File tmp = null; + try { + tmp = File.createTempFile("lib", ".so"); + prepare(agentName, tmp); + + String newAgentName = tmp.getName(); + String agent = a.substring(6).replace(agentName, newAgentName); + + ClassLoader cl = new PathClassLoader("", tmp.getParentFile().getAbsolutePath(), + Main.class.getClassLoader()); + try { + VMDebug.attachAgent(agent, cl); + } catch(IOException e) { + System.out.println(e.getMessage()); + } + } catch (Exception e) { + e.printStackTrace(System.out); + } finally { + if (tmp != null) { + tmp.delete(); + } + } + } + } + } + + private static void prepare(String in, File tmp) throws Exception { + // Find the original. + File orig = find(in); + if (orig == null) { + throw new RuntimeException("Could not find " + in); + } + // Copy the original. + { + BufferedInputStream bis = new BufferedInputStream(new FileInputStream(orig)); + BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tmp)); + byte[] buf = new byte[16 * 1024]; + for (;;) { + int r = bis.read(buf, 0, buf.length); + if (r < 0) { + break; + } else if (r > 0) { + bos.write(buf, 0, r); + } + } + bos.close(); + bis.close(); + } + } + + private static File find(String in) { + String libraryPath = System.getProperty("java.library.path"); + for (String path : libraryPath.split(":")) { + File f = new File(path + "/" + in); + if (f.exists()) { + return f; + } + } + return null; + } } |