xref: /aosp_15_r20/art/tools/jvmti-agents/chain-agents/chainagents.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker // Copyright (C) 2017 The Android Open Source Project
2*795d594fSAndroid Build Coastguard Worker //
3*795d594fSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*795d594fSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*795d594fSAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*795d594fSAndroid Build Coastguard Worker //
7*795d594fSAndroid Build Coastguard Worker //      http://www.apache.org/licenses/LICENSE-2.0
8*795d594fSAndroid Build Coastguard Worker //
9*795d594fSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*795d594fSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*795d594fSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*795d594fSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*795d594fSAndroid Build Coastguard Worker // limitations under the License.
14*795d594fSAndroid Build Coastguard Worker //
15*795d594fSAndroid Build Coastguard Worker 
16*795d594fSAndroid Build Coastguard Worker #include <android-base/logging.h>
17*795d594fSAndroid Build Coastguard Worker #include <dlfcn.h>
18*795d594fSAndroid Build Coastguard Worker #include <jni.h>
19*795d594fSAndroid Build Coastguard Worker #include <jvmti.h>
20*795d594fSAndroid Build Coastguard Worker 
21*795d594fSAndroid Build Coastguard Worker #include <atomic>
22*795d594fSAndroid Build Coastguard Worker #include <fstream>
23*795d594fSAndroid Build Coastguard Worker #include <iomanip>
24*795d594fSAndroid Build Coastguard Worker #include <iostream>
25*795d594fSAndroid Build Coastguard Worker #include <memory>
26*795d594fSAndroid Build Coastguard Worker #include <mutex>
27*795d594fSAndroid Build Coastguard Worker #include <sstream>
28*795d594fSAndroid Build Coastguard Worker #include <string>
29*795d594fSAndroid Build Coastguard Worker #include <unordered_map>
30*795d594fSAndroid Build Coastguard Worker #include <unordered_set>
31*795d594fSAndroid Build Coastguard Worker #include <vector>
32*795d594fSAndroid Build Coastguard Worker 
33*795d594fSAndroid Build Coastguard Worker namespace chainagents {
34*795d594fSAndroid Build Coastguard Worker 
35*795d594fSAndroid Build Coastguard Worker static constexpr const char* kChainFile = "chain_agents.txt";
36*795d594fSAndroid Build Coastguard Worker static constexpr const char* kOnLoad = "Agent_OnLoad";
37*795d594fSAndroid Build Coastguard Worker static constexpr const char* kOnAttach = "Agent_OnAttach";
38*795d594fSAndroid Build Coastguard Worker static constexpr const char* kOnUnload = "Agent_OnUnload";
39*795d594fSAndroid Build Coastguard Worker using AgentLoadFunction = jint (*)(JavaVM*, const char*, void*);
40*795d594fSAndroid Build Coastguard Worker using AgentUnloadFunction = jint (*)(JavaVM*);
41*795d594fSAndroid Build Coastguard Worker 
42*795d594fSAndroid Build Coastguard Worker // Global namespace. Shared by every usage of this wrapper unfortunately.
43*795d594fSAndroid Build Coastguard Worker // We need to keep track of them to call Agent_OnUnload.
44*795d594fSAndroid Build Coastguard Worker static std::mutex unload_mutex;
45*795d594fSAndroid Build Coastguard Worker 
46*795d594fSAndroid Build Coastguard Worker struct Unloader {
47*795d594fSAndroid Build Coastguard Worker   AgentUnloadFunction unload;
48*795d594fSAndroid Build Coastguard Worker };
49*795d594fSAndroid Build Coastguard Worker static std::vector<Unloader> unload_functions;
50*795d594fSAndroid Build Coastguard Worker 
51*795d594fSAndroid Build Coastguard Worker enum class StartType {
52*795d594fSAndroid Build Coastguard Worker   OnAttach,
53*795d594fSAndroid Build Coastguard Worker   OnLoad,
54*795d594fSAndroid Build Coastguard Worker };
55*795d594fSAndroid Build Coastguard Worker 
Split(const std::string & source,char delim)56*795d594fSAndroid Build Coastguard Worker static std::pair<std::string, std::string> Split(const std::string& source, char delim) {
57*795d594fSAndroid Build Coastguard Worker   std::string first(source.substr(0, source.find(delim)));
58*795d594fSAndroid Build Coastguard Worker   if (source.find(delim) == std::string::npos) {
59*795d594fSAndroid Build Coastguard Worker     return std::pair(first, "");
60*795d594fSAndroid Build Coastguard Worker   } else {
61*795d594fSAndroid Build Coastguard Worker     return std::pair(first, source.substr(source.find(delim) + 1));
62*795d594fSAndroid Build Coastguard Worker   }
63*795d594fSAndroid Build Coastguard Worker }
64*795d594fSAndroid Build Coastguard Worker 
Load(StartType start,JavaVM * vm,void * reserved,const std::pair<std::string,std::string> & lib_and_args,std::string * err)65*795d594fSAndroid Build Coastguard Worker static jint Load(StartType start,
66*795d594fSAndroid Build Coastguard Worker                  JavaVM* vm,
67*795d594fSAndroid Build Coastguard Worker                  void* reserved,
68*795d594fSAndroid Build Coastguard Worker                  const std::pair<std::string, std::string>& lib_and_args,
69*795d594fSAndroid Build Coastguard Worker                  /*out*/ std::string* err) {
70*795d594fSAndroid Build Coastguard Worker   void* handle = dlopen(lib_and_args.first.c_str(), RTLD_LAZY);
71*795d594fSAndroid Build Coastguard Worker   std::ostringstream oss;
72*795d594fSAndroid Build Coastguard Worker   if (handle == nullptr) {
73*795d594fSAndroid Build Coastguard Worker     oss << "Failed to dlopen due to " << dlerror();
74*795d594fSAndroid Build Coastguard Worker     *err = oss.str();
75*795d594fSAndroid Build Coastguard Worker     return JNI_ERR;
76*795d594fSAndroid Build Coastguard Worker   }
77*795d594fSAndroid Build Coastguard Worker   AgentLoadFunction alf = reinterpret_cast<AgentLoadFunction>(
78*795d594fSAndroid Build Coastguard Worker       dlsym(handle, start == StartType::OnLoad ? kOnLoad : kOnAttach));
79*795d594fSAndroid Build Coastguard Worker   if (alf == nullptr) {
80*795d594fSAndroid Build Coastguard Worker     oss << "Failed to dlsym " << (start == StartType::OnLoad ? kOnLoad : kOnAttach) << " due to "
81*795d594fSAndroid Build Coastguard Worker         << dlerror();
82*795d594fSAndroid Build Coastguard Worker     *err = oss.str();
83*795d594fSAndroid Build Coastguard Worker     return JNI_ERR;
84*795d594fSAndroid Build Coastguard Worker   }
85*795d594fSAndroid Build Coastguard Worker   jint res = alf(vm, lib_and_args.second.c_str(), reserved);
86*795d594fSAndroid Build Coastguard Worker   if (res != JNI_OK) {
87*795d594fSAndroid Build Coastguard Worker     *err = "load function failed!";
88*795d594fSAndroid Build Coastguard Worker     return res;
89*795d594fSAndroid Build Coastguard Worker   }
90*795d594fSAndroid Build Coastguard Worker   AgentUnloadFunction auf = reinterpret_cast<AgentUnloadFunction>(dlsym(handle, kOnUnload));
91*795d594fSAndroid Build Coastguard Worker   if (auf != nullptr) {
92*795d594fSAndroid Build Coastguard Worker     unload_functions.push_back({ auf });
93*795d594fSAndroid Build Coastguard Worker   }
94*795d594fSAndroid Build Coastguard Worker   return JNI_OK;
95*795d594fSAndroid Build Coastguard Worker }
96*795d594fSAndroid Build Coastguard Worker 
AgentStart(StartType start,JavaVM * vm,char * options,void * reserved)97*795d594fSAndroid Build Coastguard Worker static jint AgentStart(StartType start, JavaVM* vm, char* options, void* reserved) {
98*795d594fSAndroid Build Coastguard Worker   std::string input_file(options);
99*795d594fSAndroid Build Coastguard Worker   input_file = input_file + "/" + kChainFile;
100*795d594fSAndroid Build Coastguard Worker   std::ifstream input(input_file);
101*795d594fSAndroid Build Coastguard Worker   std::string line;
102*795d594fSAndroid Build Coastguard Worker   std::lock_guard<std::mutex> mu(unload_mutex);
103*795d594fSAndroid Build Coastguard Worker   while (std::getline(input, line)) {
104*795d594fSAndroid Build Coastguard Worker     std::pair<std::string, std::string> lib_and_args(Split(line, '='));
105*795d594fSAndroid Build Coastguard Worker     std::string err;
106*795d594fSAndroid Build Coastguard Worker     jint new_res = Load(start, vm, reserved, lib_and_args, &err);
107*795d594fSAndroid Build Coastguard Worker     if (new_res != JNI_OK) {
108*795d594fSAndroid Build Coastguard Worker       PLOG(WARNING) << "Failed to load library " << lib_and_args.first
109*795d594fSAndroid Build Coastguard Worker                     << " (arguments: " << lib_and_args.second << ") due to " << err;
110*795d594fSAndroid Build Coastguard Worker     }
111*795d594fSAndroid Build Coastguard Worker   }
112*795d594fSAndroid Build Coastguard Worker   return JNI_OK;
113*795d594fSAndroid Build Coastguard Worker }
114*795d594fSAndroid Build Coastguard Worker 
115*795d594fSAndroid Build Coastguard Worker // Late attachment (e.g. 'am attach-agent').
Agent_OnAttach(JavaVM * vm,char * options,void * reserved)116*795d594fSAndroid Build Coastguard Worker extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
117*795d594fSAndroid Build Coastguard Worker   return AgentStart(StartType::OnAttach, vm, options, reserved);
118*795d594fSAndroid Build Coastguard Worker }
119*795d594fSAndroid Build Coastguard Worker 
120*795d594fSAndroid Build Coastguard Worker // Early attachment
121*795d594fSAndroid Build Coastguard Worker // (e.g. 'java
122*795d594fSAndroid Build Coastguard Worker // -agentpath:/path/to/libwrapagentproperties.so=/path/to/propfile,/path/to/wrapped.so=[ops]').
Agent_OnLoad(JavaVM * jvm,char * options,void * reserved)123*795d594fSAndroid Build Coastguard Worker extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, char* options, void* reserved) {
124*795d594fSAndroid Build Coastguard Worker   return AgentStart(StartType::OnLoad, jvm, options, reserved);
125*795d594fSAndroid Build Coastguard Worker }
126*795d594fSAndroid Build Coastguard Worker 
Agent_OnUnload(JavaVM * jvm)127*795d594fSAndroid Build Coastguard Worker extern "C" JNIEXPORT void JNICALL Agent_OnUnload(JavaVM* jvm) {
128*795d594fSAndroid Build Coastguard Worker   std::lock_guard<std::mutex> lk(unload_mutex);
129*795d594fSAndroid Build Coastguard Worker   for (const Unloader& u : unload_functions) {
130*795d594fSAndroid Build Coastguard Worker     u.unload(jvm);
131*795d594fSAndroid Build Coastguard Worker     // Don't dlclose since some agents expect to still have code loaded after this.
132*795d594fSAndroid Build Coastguard Worker   }
133*795d594fSAndroid Build Coastguard Worker   unload_functions.clear();
134*795d594fSAndroid Build Coastguard Worker }
135*795d594fSAndroid Build Coastguard Worker 
136*795d594fSAndroid Build Coastguard Worker }  // namespace chainagents
137