xref: /aosp_15_r20/art/runtime/ti/agent.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2016 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  */
16*795d594fSAndroid Build Coastguard Worker 
17*795d594fSAndroid Build Coastguard Worker #include "agent.h"
18*795d594fSAndroid Build Coastguard Worker 
19*795d594fSAndroid Build Coastguard Worker #include "android-base/stringprintf.h"
20*795d594fSAndroid Build Coastguard Worker #include "nativehelper/scoped_local_ref.h"
21*795d594fSAndroid Build Coastguard Worker #include "nativeloader/native_loader.h"
22*795d594fSAndroid Build Coastguard Worker 
23*795d594fSAndroid Build Coastguard Worker #include "base/logging.h"
24*795d594fSAndroid Build Coastguard Worker #include "base/strlcpy.h"
25*795d594fSAndroid Build Coastguard Worker #include "jni/java_vm_ext.h"
26*795d594fSAndroid Build Coastguard Worker #include "runtime.h"
27*795d594fSAndroid Build Coastguard Worker #include "thread-current-inl.h"
28*795d594fSAndroid Build Coastguard Worker #include "scoped_thread_state_change-inl.h"
29*795d594fSAndroid Build Coastguard Worker 
30*795d594fSAndroid Build Coastguard Worker namespace art HIDDEN {
31*795d594fSAndroid Build Coastguard Worker namespace ti {
32*795d594fSAndroid Build Coastguard Worker 
33*795d594fSAndroid Build Coastguard Worker using android::base::StringPrintf;
34*795d594fSAndroid Build Coastguard Worker 
35*795d594fSAndroid Build Coastguard Worker const char* AGENT_ON_LOAD_FUNCTION_NAME = "Agent_OnLoad";
36*795d594fSAndroid Build Coastguard Worker const char* AGENT_ON_ATTACH_FUNCTION_NAME = "Agent_OnAttach";
37*795d594fSAndroid Build Coastguard Worker const char* AGENT_ON_UNLOAD_FUNCTION_NAME = "Agent_OnUnload";
38*795d594fSAndroid Build Coastguard Worker 
AgentSpec(const std::string & arg)39*795d594fSAndroid Build Coastguard Worker AgentSpec::AgentSpec(const std::string& arg) {
40*795d594fSAndroid Build Coastguard Worker   size_t eq = arg.find_first_of('=');
41*795d594fSAndroid Build Coastguard Worker   if (eq == std::string::npos) {
42*795d594fSAndroid Build Coastguard Worker     name_ = arg;
43*795d594fSAndroid Build Coastguard Worker   } else {
44*795d594fSAndroid Build Coastguard Worker     name_ = arg.substr(0, eq);
45*795d594fSAndroid Build Coastguard Worker     args_ = arg.substr(eq + 1, arg.length());
46*795d594fSAndroid Build Coastguard Worker   }
47*795d594fSAndroid Build Coastguard Worker }
48*795d594fSAndroid Build Coastguard Worker 
Load(jint * call_res,LoadError * error,std::string * error_msg)49*795d594fSAndroid Build Coastguard Worker std::unique_ptr<Agent> AgentSpec::Load(/*out*/jint* call_res,
50*795d594fSAndroid Build Coastguard Worker                                        /*out*/LoadError* error,
51*795d594fSAndroid Build Coastguard Worker                                        /*out*/std::string* error_msg) {
52*795d594fSAndroid Build Coastguard Worker   VLOG(agents) << "Loading agent: " << name_ << " " << args_;
53*795d594fSAndroid Build Coastguard Worker   return DoLoadHelper(nullptr, false, nullptr, call_res, error, error_msg);
54*795d594fSAndroid Build Coastguard Worker }
55*795d594fSAndroid Build Coastguard Worker 
56*795d594fSAndroid Build Coastguard Worker // Tries to attach the agent using its OnAttach method. Returns true on success.
Attach(JNIEnv * env,jobject class_loader,jint * call_res,LoadError * error,std::string * error_msg)57*795d594fSAndroid Build Coastguard Worker std::unique_ptr<Agent> AgentSpec::Attach(JNIEnv* env,
58*795d594fSAndroid Build Coastguard Worker                                          jobject class_loader,
59*795d594fSAndroid Build Coastguard Worker                                          /*out*/jint* call_res,
60*795d594fSAndroid Build Coastguard Worker                                          /*out*/LoadError* error,
61*795d594fSAndroid Build Coastguard Worker                                          /*out*/std::string* error_msg) {
62*795d594fSAndroid Build Coastguard Worker   VLOG(agents) << "Attaching agent: " << name_ << " " << args_;
63*795d594fSAndroid Build Coastguard Worker   return DoLoadHelper(env, true, class_loader, call_res, error, error_msg);
64*795d594fSAndroid Build Coastguard Worker }
65*795d594fSAndroid Build Coastguard Worker 
66*795d594fSAndroid Build Coastguard Worker 
67*795d594fSAndroid Build Coastguard Worker // TODO We need to acquire some locks probably.
DoLoadHelper(JNIEnv * env,bool attaching,jobject class_loader,jint * call_res,LoadError * error,std::string * error_msg)68*795d594fSAndroid Build Coastguard Worker std::unique_ptr<Agent> AgentSpec::DoLoadHelper(JNIEnv* env,
69*795d594fSAndroid Build Coastguard Worker                                                bool attaching,
70*795d594fSAndroid Build Coastguard Worker                                                jobject class_loader,
71*795d594fSAndroid Build Coastguard Worker                                                /*out*/jint* call_res,
72*795d594fSAndroid Build Coastguard Worker                                                /*out*/LoadError* error,
73*795d594fSAndroid Build Coastguard Worker                                                /*out*/std::string* error_msg) {
74*795d594fSAndroid Build Coastguard Worker   ScopedThreadStateChange stsc(Thread::Current(), ThreadState::kNative);
75*795d594fSAndroid Build Coastguard Worker   DCHECK(call_res != nullptr);
76*795d594fSAndroid Build Coastguard Worker   DCHECK(error_msg != nullptr);
77*795d594fSAndroid Build Coastguard Worker 
78*795d594fSAndroid Build Coastguard Worker   std::unique_ptr<Agent> agent = DoDlOpen(env, class_loader, error, error_msg);
79*795d594fSAndroid Build Coastguard Worker   if (agent == nullptr) {
80*795d594fSAndroid Build Coastguard Worker     VLOG(agents) << "err: " << *error_msg;
81*795d594fSAndroid Build Coastguard Worker     return nullptr;
82*795d594fSAndroid Build Coastguard Worker   }
83*795d594fSAndroid Build Coastguard Worker   AgentOnLoadFunction callback = attaching ? agent->onattach_ : agent->onload_;
84*795d594fSAndroid Build Coastguard Worker   if (callback == nullptr) {
85*795d594fSAndroid Build Coastguard Worker     *error_msg = StringPrintf("Unable to start agent %s: No %s callback found",
86*795d594fSAndroid Build Coastguard Worker                               (attaching ? "attach" : "load"),
87*795d594fSAndroid Build Coastguard Worker                               name_.c_str());
88*795d594fSAndroid Build Coastguard Worker     VLOG(agents) << "err: " << *error_msg;
89*795d594fSAndroid Build Coastguard Worker     *error = kLoadingError;
90*795d594fSAndroid Build Coastguard Worker     return nullptr;
91*795d594fSAndroid Build Coastguard Worker   }
92*795d594fSAndroid Build Coastguard Worker   // Need to let the function fiddle with the array.
93*795d594fSAndroid Build Coastguard Worker   std::unique_ptr<char[]> copied_args(new char[args_.size() + 1]);
94*795d594fSAndroid Build Coastguard Worker   strlcpy(copied_args.get(), args_.c_str(), args_.size() + 1);
95*795d594fSAndroid Build Coastguard Worker   // TODO Need to do some checks that we are at a good spot etc.
96*795d594fSAndroid Build Coastguard Worker   *call_res = callback(Runtime::Current()->GetJavaVM(),
97*795d594fSAndroid Build Coastguard Worker                        copied_args.get(),
98*795d594fSAndroid Build Coastguard Worker                        nullptr);
99*795d594fSAndroid Build Coastguard Worker   if (*call_res != 0) {
100*795d594fSAndroid Build Coastguard Worker     *error_msg = StringPrintf("Initialization of %s returned non-zero value of %d",
101*795d594fSAndroid Build Coastguard Worker                               name_.c_str(), *call_res);
102*795d594fSAndroid Build Coastguard Worker     VLOG(agents) << "err: " << *error_msg;
103*795d594fSAndroid Build Coastguard Worker     *error = kInitializationError;
104*795d594fSAndroid Build Coastguard Worker     return nullptr;
105*795d594fSAndroid Build Coastguard Worker   }
106*795d594fSAndroid Build Coastguard Worker   return agent;
107*795d594fSAndroid Build Coastguard Worker }
108*795d594fSAndroid Build Coastguard Worker 
DoDlOpen(JNIEnv * env,jobject class_loader,LoadError * error,std::string * error_msg)109*795d594fSAndroid Build Coastguard Worker std::unique_ptr<Agent> AgentSpec::DoDlOpen(JNIEnv* env,
110*795d594fSAndroid Build Coastguard Worker                                            jobject class_loader,
111*795d594fSAndroid Build Coastguard Worker                                            /*out*/LoadError* error,
112*795d594fSAndroid Build Coastguard Worker                                            /*out*/std::string* error_msg) {
113*795d594fSAndroid Build Coastguard Worker   DCHECK(error_msg != nullptr);
114*795d594fSAndroid Build Coastguard Worker 
115*795d594fSAndroid Build Coastguard Worker   ScopedLocalRef<jstring> library_path(env,
116*795d594fSAndroid Build Coastguard Worker                                        class_loader == nullptr
117*795d594fSAndroid Build Coastguard Worker                                            ? nullptr
118*795d594fSAndroid Build Coastguard Worker                                            : JavaVMExt::GetLibrarySearchPath(env, class_loader));
119*795d594fSAndroid Build Coastguard Worker 
120*795d594fSAndroid Build Coastguard Worker   bool needs_native_bridge = false;
121*795d594fSAndroid Build Coastguard Worker   char* nativeloader_error_msg = nullptr;
122*795d594fSAndroid Build Coastguard Worker   void* dlopen_handle = android::OpenNativeLibrary(env,
123*795d594fSAndroid Build Coastguard Worker                                                    Runtime::Current()->GetTargetSdkVersion(),
124*795d594fSAndroid Build Coastguard Worker                                                    name_.c_str(),
125*795d594fSAndroid Build Coastguard Worker                                                    class_loader,
126*795d594fSAndroid Build Coastguard Worker                                                    nullptr,
127*795d594fSAndroid Build Coastguard Worker                                                    library_path.get(),
128*795d594fSAndroid Build Coastguard Worker                                                    &needs_native_bridge,
129*795d594fSAndroid Build Coastguard Worker                                                    &nativeloader_error_msg);
130*795d594fSAndroid Build Coastguard Worker   if (dlopen_handle == nullptr) {
131*795d594fSAndroid Build Coastguard Worker     *error_msg = StringPrintf("Unable to dlopen %s: %s",
132*795d594fSAndroid Build Coastguard Worker                               name_.c_str(),
133*795d594fSAndroid Build Coastguard Worker                               nativeloader_error_msg);
134*795d594fSAndroid Build Coastguard Worker     android::NativeLoaderFreeErrorMessage(nativeloader_error_msg);
135*795d594fSAndroid Build Coastguard Worker     *error = kLoadingError;
136*795d594fSAndroid Build Coastguard Worker     return nullptr;
137*795d594fSAndroid Build Coastguard Worker   }
138*795d594fSAndroid Build Coastguard Worker   if (needs_native_bridge) {
139*795d594fSAndroid Build Coastguard Worker     // TODO: Consider support?
140*795d594fSAndroid Build Coastguard Worker     // The result of this call and error_msg is ignored because the most
141*795d594fSAndroid Build Coastguard Worker     // relevant error is that native bridge is unsupported.
142*795d594fSAndroid Build Coastguard Worker     android::CloseNativeLibrary(dlopen_handle, needs_native_bridge, &nativeloader_error_msg);
143*795d594fSAndroid Build Coastguard Worker     android::NativeLoaderFreeErrorMessage(nativeloader_error_msg);
144*795d594fSAndroid Build Coastguard Worker     *error_msg = StringPrintf("Native-bridge agents unsupported: %s", name_.c_str());
145*795d594fSAndroid Build Coastguard Worker     *error = kLoadingError;
146*795d594fSAndroid Build Coastguard Worker     return nullptr;
147*795d594fSAndroid Build Coastguard Worker   }
148*795d594fSAndroid Build Coastguard Worker 
149*795d594fSAndroid Build Coastguard Worker   std::unique_ptr<Agent> agent(new Agent(name_, dlopen_handle));
150*795d594fSAndroid Build Coastguard Worker   agent->PopulateFunctions();
151*795d594fSAndroid Build Coastguard Worker   *error = kNoError;
152*795d594fSAndroid Build Coastguard Worker   return agent;
153*795d594fSAndroid Build Coastguard Worker }
154*795d594fSAndroid Build Coastguard Worker 
operator <<(std::ostream & os,AgentSpec const & m)155*795d594fSAndroid Build Coastguard Worker std::ostream& operator<<(std::ostream &os, AgentSpec const& m) {
156*795d594fSAndroid Build Coastguard Worker   return os << "AgentSpec { name=\"" << m.name_ << "\", args=\"" << m.args_ << "\" }";
157*795d594fSAndroid Build Coastguard Worker }
158*795d594fSAndroid Build Coastguard Worker 
159*795d594fSAndroid Build Coastguard Worker 
FindSymbol(const std::string & name) const160*795d594fSAndroid Build Coastguard Worker void* Agent::FindSymbol(const std::string& name) const {
161*795d594fSAndroid Build Coastguard Worker   CHECK(dlopen_handle_ != nullptr) << "Cannot find symbols in an unloaded agent library " << this;
162*795d594fSAndroid Build Coastguard Worker   return dlsym(dlopen_handle_, name.c_str());
163*795d594fSAndroid Build Coastguard Worker }
164*795d594fSAndroid Build Coastguard Worker 
165*795d594fSAndroid Build Coastguard Worker // TODO Lock some stuff probably.
Unload()166*795d594fSAndroid Build Coastguard Worker void Agent::Unload() {
167*795d594fSAndroid Build Coastguard Worker   if (dlopen_handle_ != nullptr) {
168*795d594fSAndroid Build Coastguard Worker     if (onunload_ != nullptr) {
169*795d594fSAndroid Build Coastguard Worker       onunload_(Runtime::Current()->GetJavaVM());
170*795d594fSAndroid Build Coastguard Worker     }
171*795d594fSAndroid Build Coastguard Worker     // Don't actually android::CloseNativeLibrary since some agents assume they will never get
172*795d594fSAndroid Build Coastguard Worker     // unloaded. Since this only happens when the runtime is shutting down anyway this isn't a big
173*795d594fSAndroid Build Coastguard Worker     // deal.
174*795d594fSAndroid Build Coastguard Worker     dlopen_handle_ = nullptr;
175*795d594fSAndroid Build Coastguard Worker     onload_ = nullptr;
176*795d594fSAndroid Build Coastguard Worker     onattach_ = nullptr;
177*795d594fSAndroid Build Coastguard Worker     onunload_ = nullptr;
178*795d594fSAndroid Build Coastguard Worker   } else {
179*795d594fSAndroid Build Coastguard Worker     VLOG(agents) << this << " is not currently loaded!";
180*795d594fSAndroid Build Coastguard Worker   }
181*795d594fSAndroid Build Coastguard Worker }
182*795d594fSAndroid Build Coastguard Worker 
Agent(Agent && other)183*795d594fSAndroid Build Coastguard Worker Agent::Agent(Agent&& other) noexcept
184*795d594fSAndroid Build Coastguard Worker     : dlopen_handle_(nullptr),
185*795d594fSAndroid Build Coastguard Worker       onload_(nullptr),
186*795d594fSAndroid Build Coastguard Worker       onattach_(nullptr),
187*795d594fSAndroid Build Coastguard Worker       onunload_(nullptr) {
188*795d594fSAndroid Build Coastguard Worker   *this = std::move(other);
189*795d594fSAndroid Build Coastguard Worker }
190*795d594fSAndroid Build Coastguard Worker 
operator =(Agent && other)191*795d594fSAndroid Build Coastguard Worker Agent& Agent::operator=(Agent&& other) noexcept {
192*795d594fSAndroid Build Coastguard Worker   if (this != &other) {
193*795d594fSAndroid Build Coastguard Worker     if (dlopen_handle_ != nullptr) {
194*795d594fSAndroid Build Coastguard Worker       Unload();
195*795d594fSAndroid Build Coastguard Worker     }
196*795d594fSAndroid Build Coastguard Worker     name_ = std::move(other.name_);
197*795d594fSAndroid Build Coastguard Worker     dlopen_handle_ = other.dlopen_handle_;
198*795d594fSAndroid Build Coastguard Worker     onload_ = other.onload_;
199*795d594fSAndroid Build Coastguard Worker     onattach_ = other.onattach_;
200*795d594fSAndroid Build Coastguard Worker     onunload_ = other.onunload_;
201*795d594fSAndroid Build Coastguard Worker     other.dlopen_handle_ = nullptr;
202*795d594fSAndroid Build Coastguard Worker     other.onload_ = nullptr;
203*795d594fSAndroid Build Coastguard Worker     other.onattach_ = nullptr;
204*795d594fSAndroid Build Coastguard Worker     other.onunload_ = nullptr;
205*795d594fSAndroid Build Coastguard Worker   }
206*795d594fSAndroid Build Coastguard Worker   return *this;
207*795d594fSAndroid Build Coastguard Worker }
208*795d594fSAndroid Build Coastguard Worker 
PopulateFunctions()209*795d594fSAndroid Build Coastguard Worker void Agent::PopulateFunctions() {
210*795d594fSAndroid Build Coastguard Worker   onload_ = reinterpret_cast<AgentOnLoadFunction>(FindSymbol(AGENT_ON_LOAD_FUNCTION_NAME));
211*795d594fSAndroid Build Coastguard Worker   if (onload_ == nullptr) {
212*795d594fSAndroid Build Coastguard Worker     VLOG(agents) << "Unable to find 'Agent_OnLoad' symbol in " << this;
213*795d594fSAndroid Build Coastguard Worker   }
214*795d594fSAndroid Build Coastguard Worker   onattach_ = reinterpret_cast<AgentOnLoadFunction>(FindSymbol(AGENT_ON_ATTACH_FUNCTION_NAME));
215*795d594fSAndroid Build Coastguard Worker   if (onattach_ == nullptr) {
216*795d594fSAndroid Build Coastguard Worker     VLOG(agents) << "Unable to find 'Agent_OnAttach' symbol in " << this;
217*795d594fSAndroid Build Coastguard Worker   }
218*795d594fSAndroid Build Coastguard Worker   onunload_ = reinterpret_cast<AgentOnUnloadFunction>(FindSymbol(AGENT_ON_UNLOAD_FUNCTION_NAME));
219*795d594fSAndroid Build Coastguard Worker   if (onunload_ == nullptr) {
220*795d594fSAndroid Build Coastguard Worker     VLOG(agents) << "Unable to find 'Agent_OnUnload' symbol in " << this;
221*795d594fSAndroid Build Coastguard Worker   }
222*795d594fSAndroid Build Coastguard Worker }
223*795d594fSAndroid Build Coastguard Worker 
~Agent()224*795d594fSAndroid Build Coastguard Worker Agent::~Agent() {
225*795d594fSAndroid Build Coastguard Worker   if (dlopen_handle_ != nullptr) {
226*795d594fSAndroid Build Coastguard Worker     Unload();
227*795d594fSAndroid Build Coastguard Worker   }
228*795d594fSAndroid Build Coastguard Worker }
229*795d594fSAndroid Build Coastguard Worker 
operator <<(std::ostream & os,const Agent * m)230*795d594fSAndroid Build Coastguard Worker std::ostream& operator<<(std::ostream &os, const Agent* m) {
231*795d594fSAndroid Build Coastguard Worker   return os << *m;
232*795d594fSAndroid Build Coastguard Worker }
233*795d594fSAndroid Build Coastguard Worker 
operator <<(std::ostream & os,Agent const & m)234*795d594fSAndroid Build Coastguard Worker std::ostream& operator<<(std::ostream &os, Agent const& m) {
235*795d594fSAndroid Build Coastguard Worker   return os << "Agent { name=\"" << m.name_ << "\", handle=" << m.dlopen_handle_ << " }";
236*795d594fSAndroid Build Coastguard Worker }
237*795d594fSAndroid Build Coastguard Worker 
238*795d594fSAndroid Build Coastguard Worker }  // namespace ti
239*795d594fSAndroid Build Coastguard Worker }  // namespace art
240