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 <atomic>
18*795d594fSAndroid Build Coastguard Worker #include <dlfcn.h>
19*795d594fSAndroid Build Coastguard Worker #include <iostream>
20*795d594fSAndroid Build Coastguard Worker #include <fstream>
21*795d594fSAndroid Build Coastguard Worker #include <iomanip>
22*795d594fSAndroid Build Coastguard Worker #include <jni.h>
23*795d594fSAndroid Build Coastguard Worker #include <jvmti.h>
24*795d594fSAndroid Build Coastguard Worker #include <unordered_map>
25*795d594fSAndroid Build Coastguard Worker #include <unordered_set>
26*795d594fSAndroid Build Coastguard Worker #include <memory>
27*795d594fSAndroid Build Coastguard Worker #include <mutex>
28*795d594fSAndroid Build Coastguard Worker #include <sstream>
29*795d594fSAndroid Build Coastguard Worker #include <string>
30*795d594fSAndroid Build Coastguard Worker #include <vector>
31*795d594fSAndroid Build Coastguard Worker
32*795d594fSAndroid Build Coastguard Worker namespace wrapagentproperties {
33*795d594fSAndroid Build Coastguard Worker
34*795d594fSAndroid Build Coastguard Worker using PropMap = std::unordered_map<std::string, std::string>;
35*795d594fSAndroid Build Coastguard Worker static constexpr const char* kOnLoad = "Agent_OnLoad";
36*795d594fSAndroid Build Coastguard Worker static constexpr const char* kOnAttach = "Agent_OnAttach";
37*795d594fSAndroid Build Coastguard Worker static constexpr const char* kOnUnload = "Agent_OnUnload";
38*795d594fSAndroid Build Coastguard Worker struct ProxyJavaVM;
39*795d594fSAndroid Build Coastguard Worker using AgentLoadFunction = jint (*)(ProxyJavaVM*, 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 static jint CreateJvmtiEnv(ProxyJavaVM* vm, void** out_env, jint version);
52*795d594fSAndroid Build Coastguard Worker
53*795d594fSAndroid Build Coastguard Worker struct ProxyJavaVM {
54*795d594fSAndroid Build Coastguard Worker const struct JNIInvokeInterface* functions;
55*795d594fSAndroid Build Coastguard Worker JavaVM* real_vm;
56*795d594fSAndroid Build Coastguard Worker PropMap* map;
57*795d594fSAndroid Build Coastguard Worker void* dlopen_handle;
58*795d594fSAndroid Build Coastguard Worker AgentLoadFunction load;
59*795d594fSAndroid Build Coastguard Worker AgentLoadFunction attach;
60*795d594fSAndroid Build Coastguard Worker
ProxyJavaVMwrapagentproperties::ProxyJavaVM61*795d594fSAndroid Build Coastguard Worker ProxyJavaVM(JavaVM* vm, const std::string& agent_lib, PropMap* map)
62*795d594fSAndroid Build Coastguard Worker : functions(CreateInvokeInterface()),
63*795d594fSAndroid Build Coastguard Worker real_vm(vm),
64*795d594fSAndroid Build Coastguard Worker map(map),
65*795d594fSAndroid Build Coastguard Worker dlopen_handle(dlopen(agent_lib.c_str(), RTLD_LAZY)),
66*795d594fSAndroid Build Coastguard Worker load(nullptr),
67*795d594fSAndroid Build Coastguard Worker attach(nullptr) {
68*795d594fSAndroid Build Coastguard Worker CHECK(dlopen_handle != nullptr) << "unable to open " << agent_lib;
69*795d594fSAndroid Build Coastguard Worker {
70*795d594fSAndroid Build Coastguard Worker std::lock_guard<std::mutex> lk(unload_mutex);
71*795d594fSAndroid Build Coastguard Worker unload_functions.push_back({
72*795d594fSAndroid Build Coastguard Worker reinterpret_cast<AgentUnloadFunction>(dlsym(dlopen_handle, kOnUnload)),
73*795d594fSAndroid Build Coastguard Worker });
74*795d594fSAndroid Build Coastguard Worker }
75*795d594fSAndroid Build Coastguard Worker attach = reinterpret_cast<AgentLoadFunction>(dlsym(dlopen_handle, kOnAttach));
76*795d594fSAndroid Build Coastguard Worker load = reinterpret_cast<AgentLoadFunction>(dlsym(dlopen_handle, kOnLoad));
77*795d594fSAndroid Build Coastguard Worker }
78*795d594fSAndroid Build Coastguard Worker
79*795d594fSAndroid Build Coastguard Worker // TODO Use this to cleanup
WrapDestroyJavaVMwrapagentproperties::ProxyJavaVM80*795d594fSAndroid Build Coastguard Worker static jint WrapDestroyJavaVM(ProxyJavaVM* vm) {
81*795d594fSAndroid Build Coastguard Worker return vm->real_vm->DestroyJavaVM();
82*795d594fSAndroid Build Coastguard Worker }
WrapAttachCurrentThreadwrapagentproperties::ProxyJavaVM83*795d594fSAndroid Build Coastguard Worker static jint WrapAttachCurrentThread(ProxyJavaVM* vm, JNIEnv** env, void* res) {
84*795d594fSAndroid Build Coastguard Worker return vm->real_vm->AttachCurrentThread(env, res);
85*795d594fSAndroid Build Coastguard Worker }
WrapDetachCurrentThreadwrapagentproperties::ProxyJavaVM86*795d594fSAndroid Build Coastguard Worker static jint WrapDetachCurrentThread(ProxyJavaVM* vm) {
87*795d594fSAndroid Build Coastguard Worker return vm->real_vm->DetachCurrentThread();
88*795d594fSAndroid Build Coastguard Worker }
WrapAttachCurrentThreadAsDaemonwrapagentproperties::ProxyJavaVM89*795d594fSAndroid Build Coastguard Worker static jint WrapAttachCurrentThreadAsDaemon(ProxyJavaVM* vm, JNIEnv** env, void* res) {
90*795d594fSAndroid Build Coastguard Worker return vm->real_vm->AttachCurrentThreadAsDaemon(env, res);
91*795d594fSAndroid Build Coastguard Worker }
92*795d594fSAndroid Build Coastguard Worker
WrapGetEnvwrapagentproperties::ProxyJavaVM93*795d594fSAndroid Build Coastguard Worker static jint WrapGetEnv(ProxyJavaVM* vm, void** out_env, jint version) {
94*795d594fSAndroid Build Coastguard Worker switch (version) {
95*795d594fSAndroid Build Coastguard Worker case JVMTI_VERSION:
96*795d594fSAndroid Build Coastguard Worker case JVMTI_VERSION_1:
97*795d594fSAndroid Build Coastguard Worker case JVMTI_VERSION_1_1:
98*795d594fSAndroid Build Coastguard Worker case JVMTI_VERSION_1_2:
99*795d594fSAndroid Build Coastguard Worker return CreateJvmtiEnv(vm, out_env, version);
100*795d594fSAndroid Build Coastguard Worker default:
101*795d594fSAndroid Build Coastguard Worker if ((version & 0x30000000) == 0x30000000) {
102*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "Version number 0x" << std::hex << version << " looks like a JVMTI "
103*795d594fSAndroid Build Coastguard Worker << "version but it is not one that is recognized. The wrapper might not "
104*795d594fSAndroid Build Coastguard Worker << "function correctly! Continuing anyway.";
105*795d594fSAndroid Build Coastguard Worker }
106*795d594fSAndroid Build Coastguard Worker return vm->real_vm->GetEnv(out_env, version);
107*795d594fSAndroid Build Coastguard Worker }
108*795d594fSAndroid Build Coastguard Worker }
109*795d594fSAndroid Build Coastguard Worker
CreateInvokeInterfacewrapagentproperties::ProxyJavaVM110*795d594fSAndroid Build Coastguard Worker static JNIInvokeInterface* CreateInvokeInterface() {
111*795d594fSAndroid Build Coastguard Worker JNIInvokeInterface* out = new JNIInvokeInterface;
112*795d594fSAndroid Build Coastguard Worker memset(out, 0, sizeof(JNIInvokeInterface));
113*795d594fSAndroid Build Coastguard Worker out->DestroyJavaVM = reinterpret_cast<jint (*)(JavaVM*)>(WrapDestroyJavaVM);
114*795d594fSAndroid Build Coastguard Worker out->AttachCurrentThread =
115*795d594fSAndroid Build Coastguard Worker reinterpret_cast<jint(*)(JavaVM*, JNIEnv**, void*)>(WrapAttachCurrentThread);
116*795d594fSAndroid Build Coastguard Worker out->DetachCurrentThread = reinterpret_cast<jint(*)(JavaVM*)>(WrapDetachCurrentThread);
117*795d594fSAndroid Build Coastguard Worker out->GetEnv = reinterpret_cast<jint(*)(JavaVM*, void**, jint)>(WrapGetEnv);
118*795d594fSAndroid Build Coastguard Worker out->AttachCurrentThreadAsDaemon =
119*795d594fSAndroid Build Coastguard Worker reinterpret_cast<jint(*)(JavaVM*, JNIEnv**, void*)>(WrapAttachCurrentThreadAsDaemon);
120*795d594fSAndroid Build Coastguard Worker return out;
121*795d594fSAndroid Build Coastguard Worker }
122*795d594fSAndroid Build Coastguard Worker };
123*795d594fSAndroid Build Coastguard Worker
124*795d594fSAndroid Build Coastguard Worker
125*795d594fSAndroid Build Coastguard Worker struct ExtraJvmtiInterface : public jvmtiInterface_1_ {
126*795d594fSAndroid Build Coastguard Worker ProxyJavaVM* proxy_vm;
127*795d594fSAndroid Build Coastguard Worker jvmtiInterface_1_ const* original_interface;
128*795d594fSAndroid Build Coastguard Worker
WrapDisposeEnvironmentwrapagentproperties::ExtraJvmtiInterface129*795d594fSAndroid Build Coastguard Worker static jvmtiError WrapDisposeEnvironment(jvmtiEnv* env) {
130*795d594fSAndroid Build Coastguard Worker ExtraJvmtiInterface* funcs = reinterpret_cast<ExtraJvmtiInterface*>(
131*795d594fSAndroid Build Coastguard Worker const_cast<jvmtiInterface_1_*>(env->functions));
132*795d594fSAndroid Build Coastguard Worker jvmtiInterface_1_** out_iface = const_cast<jvmtiInterface_1_**>(&env->functions);
133*795d594fSAndroid Build Coastguard Worker *out_iface = const_cast<jvmtiInterface_1_*>(funcs->original_interface);
134*795d594fSAndroid Build Coastguard Worker funcs->original_interface->Deallocate(env, reinterpret_cast<unsigned char*>(funcs));
135*795d594fSAndroid Build Coastguard Worker jvmtiError res = (*out_iface)->DisposeEnvironment(env);
136*795d594fSAndroid Build Coastguard Worker return res;
137*795d594fSAndroid Build Coastguard Worker }
138*795d594fSAndroid Build Coastguard Worker
WrapGetSystemPropertywrapagentproperties::ExtraJvmtiInterface139*795d594fSAndroid Build Coastguard Worker static jvmtiError WrapGetSystemProperty(jvmtiEnv* env, const char* prop, char** out) {
140*795d594fSAndroid Build Coastguard Worker ExtraJvmtiInterface* funcs = reinterpret_cast<ExtraJvmtiInterface*>(
141*795d594fSAndroid Build Coastguard Worker const_cast<jvmtiInterface_1_*>(env->functions));
142*795d594fSAndroid Build Coastguard Worker auto it = funcs->proxy_vm->map->find(prop);
143*795d594fSAndroid Build Coastguard Worker if (it != funcs->proxy_vm->map->end()) {
144*795d594fSAndroid Build Coastguard Worker const std::string& val = it->second;
145*795d594fSAndroid Build Coastguard Worker std::string str_prop(prop);
146*795d594fSAndroid Build Coastguard Worker jvmtiError res = env->Allocate(val.size() + 1, reinterpret_cast<unsigned char**>(out));
147*795d594fSAndroid Build Coastguard Worker if (res != JVMTI_ERROR_NONE) {
148*795d594fSAndroid Build Coastguard Worker return res;
149*795d594fSAndroid Build Coastguard Worker }
150*795d594fSAndroid Build Coastguard Worker strcpy(*out, val.c_str());
151*795d594fSAndroid Build Coastguard Worker return JVMTI_ERROR_NONE;
152*795d594fSAndroid Build Coastguard Worker } else {
153*795d594fSAndroid Build Coastguard Worker return funcs->original_interface->GetSystemProperty(env, prop, out);
154*795d594fSAndroid Build Coastguard Worker }
155*795d594fSAndroid Build Coastguard Worker }
156*795d594fSAndroid Build Coastguard Worker
WrapGetSystemPropertieswrapagentproperties::ExtraJvmtiInterface157*795d594fSAndroid Build Coastguard Worker static jvmtiError WrapGetSystemProperties(jvmtiEnv* env, jint* cnt, char*** prop_ptr) {
158*795d594fSAndroid Build Coastguard Worker ExtraJvmtiInterface* funcs = reinterpret_cast<ExtraJvmtiInterface*>(
159*795d594fSAndroid Build Coastguard Worker const_cast<jvmtiInterface_1_*>(env->functions));
160*795d594fSAndroid Build Coastguard Worker jint init_cnt;
161*795d594fSAndroid Build Coastguard Worker char** init_prop_ptr;
162*795d594fSAndroid Build Coastguard Worker jvmtiError res = funcs->original_interface->GetSystemProperties(env, &init_cnt, &init_prop_ptr);
163*795d594fSAndroid Build Coastguard Worker if (res != JVMTI_ERROR_NONE) {
164*795d594fSAndroid Build Coastguard Worker return res;
165*795d594fSAndroid Build Coastguard Worker }
166*795d594fSAndroid Build Coastguard Worker std::unordered_set<std::string> all_props;
167*795d594fSAndroid Build Coastguard Worker for (const auto& p : *funcs->proxy_vm->map) {
168*795d594fSAndroid Build Coastguard Worker all_props.insert(p.first);
169*795d594fSAndroid Build Coastguard Worker }
170*795d594fSAndroid Build Coastguard Worker for (jint i = 0; i < init_cnt; i++) {
171*795d594fSAndroid Build Coastguard Worker all_props.insert(init_prop_ptr[i]);
172*795d594fSAndroid Build Coastguard Worker env->Deallocate(reinterpret_cast<unsigned char*>(init_prop_ptr[i]));
173*795d594fSAndroid Build Coastguard Worker }
174*795d594fSAndroid Build Coastguard Worker env->Deallocate(reinterpret_cast<unsigned char*>(init_prop_ptr));
175*795d594fSAndroid Build Coastguard Worker *cnt = all_props.size();
176*795d594fSAndroid Build Coastguard Worker res = env->Allocate(all_props.size() * sizeof(char*),
177*795d594fSAndroid Build Coastguard Worker reinterpret_cast<unsigned char**>(prop_ptr));
178*795d594fSAndroid Build Coastguard Worker if (res != JVMTI_ERROR_NONE) {
179*795d594fSAndroid Build Coastguard Worker return res;
180*795d594fSAndroid Build Coastguard Worker }
181*795d594fSAndroid Build Coastguard Worker char** out_prop_ptr = *prop_ptr;
182*795d594fSAndroid Build Coastguard Worker jint i = 0;
183*795d594fSAndroid Build Coastguard Worker for (const std::string& p : all_props) {
184*795d594fSAndroid Build Coastguard Worker res = env->Allocate(p.size() + 1, reinterpret_cast<unsigned char**>(&out_prop_ptr[i]));
185*795d594fSAndroid Build Coastguard Worker if (res != JVMTI_ERROR_NONE) {
186*795d594fSAndroid Build Coastguard Worker return res;
187*795d594fSAndroid Build Coastguard Worker }
188*795d594fSAndroid Build Coastguard Worker strcpy(out_prop_ptr[i], p.c_str());
189*795d594fSAndroid Build Coastguard Worker i++;
190*795d594fSAndroid Build Coastguard Worker }
191*795d594fSAndroid Build Coastguard Worker CHECK_EQ(i, *cnt);
192*795d594fSAndroid Build Coastguard Worker return JVMTI_ERROR_NONE;
193*795d594fSAndroid Build Coastguard Worker }
194*795d594fSAndroid Build Coastguard Worker
WrapSetSystemPropertywrapagentproperties::ExtraJvmtiInterface195*795d594fSAndroid Build Coastguard Worker static jvmtiError WrapSetSystemProperty(jvmtiEnv* env, const char* prop, const char* val) {
196*795d594fSAndroid Build Coastguard Worker ExtraJvmtiInterface* funcs = reinterpret_cast<ExtraJvmtiInterface*>(
197*795d594fSAndroid Build Coastguard Worker const_cast<jvmtiInterface_1_*>(env->functions));
198*795d594fSAndroid Build Coastguard Worker jvmtiError res = funcs->original_interface->SetSystemProperty(env, prop, val);
199*795d594fSAndroid Build Coastguard Worker if (res != JVMTI_ERROR_NONE) {
200*795d594fSAndroid Build Coastguard Worker return res;
201*795d594fSAndroid Build Coastguard Worker }
202*795d594fSAndroid Build Coastguard Worker auto it = funcs->proxy_vm->map->find(prop);
203*795d594fSAndroid Build Coastguard Worker if (it != funcs->proxy_vm->map->end()) {
204*795d594fSAndroid Build Coastguard Worker it->second = val;
205*795d594fSAndroid Build Coastguard Worker }
206*795d594fSAndroid Build Coastguard Worker return JVMTI_ERROR_NONE;
207*795d594fSAndroid Build Coastguard Worker }
208*795d594fSAndroid Build Coastguard Worker
209*795d594fSAndroid Build Coastguard Worker // TODO It would be way better to actually set up a full proxy like we did for JavaVM but the
210*795d594fSAndroid Build Coastguard Worker // number of functions makes it not worth it.
SetupProxyJvmtiEnvwrapagentproperties::ExtraJvmtiInterface211*795d594fSAndroid Build Coastguard Worker static jint SetupProxyJvmtiEnv(ProxyJavaVM* vm, jvmtiEnv* real_env) {
212*795d594fSAndroid Build Coastguard Worker ExtraJvmtiInterface* new_iface = nullptr;
213*795d594fSAndroid Build Coastguard Worker if (JVMTI_ERROR_NONE != real_env->Allocate(sizeof(ExtraJvmtiInterface),
214*795d594fSAndroid Build Coastguard Worker reinterpret_cast<unsigned char**>(&new_iface))) {
215*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "Could not allocate extra space for new jvmti interface struct";
216*795d594fSAndroid Build Coastguard Worker return JNI_ERR;
217*795d594fSAndroid Build Coastguard Worker }
218*795d594fSAndroid Build Coastguard Worker memcpy(new_iface, real_env->functions, sizeof(jvmtiInterface_1_));
219*795d594fSAndroid Build Coastguard Worker new_iface->proxy_vm = vm;
220*795d594fSAndroid Build Coastguard Worker new_iface->original_interface = real_env->functions;
221*795d594fSAndroid Build Coastguard Worker
222*795d594fSAndroid Build Coastguard Worker // Replace these functions with the new ones.
223*795d594fSAndroid Build Coastguard Worker new_iface->DisposeEnvironment = WrapDisposeEnvironment;
224*795d594fSAndroid Build Coastguard Worker new_iface->GetSystemProperty = WrapGetSystemProperty;
225*795d594fSAndroid Build Coastguard Worker new_iface->GetSystemProperties = WrapGetSystemProperties;
226*795d594fSAndroid Build Coastguard Worker new_iface->SetSystemProperty = WrapSetSystemProperty;
227*795d594fSAndroid Build Coastguard Worker
228*795d594fSAndroid Build Coastguard Worker // Replace the functions table with our new one with replaced functions.
229*795d594fSAndroid Build Coastguard Worker jvmtiInterface_1_** out_iface = const_cast<jvmtiInterface_1_**>(&real_env->functions);
230*795d594fSAndroid Build Coastguard Worker *out_iface = new_iface;
231*795d594fSAndroid Build Coastguard Worker return JNI_OK;
232*795d594fSAndroid Build Coastguard Worker }
233*795d594fSAndroid Build Coastguard Worker };
234*795d594fSAndroid Build Coastguard Worker
CreateJvmtiEnv(ProxyJavaVM * vm,void ** out_env,jint version)235*795d594fSAndroid Build Coastguard Worker static jint CreateJvmtiEnv(ProxyJavaVM* vm, void** out_env, jint version) {
236*795d594fSAndroid Build Coastguard Worker jint res = vm->real_vm->GetEnv(out_env, version);
237*795d594fSAndroid Build Coastguard Worker if (res != JNI_OK) {
238*795d594fSAndroid Build Coastguard Worker LOG(WARNING) << "Could not create jvmtiEnv to proxy!";
239*795d594fSAndroid Build Coastguard Worker return res;
240*795d594fSAndroid Build Coastguard Worker }
241*795d594fSAndroid Build Coastguard Worker return ExtraJvmtiInterface::SetupProxyJvmtiEnv(vm, reinterpret_cast<jvmtiEnv*>(*out_env));
242*795d594fSAndroid Build Coastguard Worker }
243*795d594fSAndroid Build Coastguard Worker
244*795d594fSAndroid Build Coastguard Worker enum class StartType {
245*795d594fSAndroid Build Coastguard Worker OnAttach, OnLoad,
246*795d594fSAndroid Build Coastguard Worker };
247*795d594fSAndroid Build Coastguard Worker
CallNextAgent(StartType start,ProxyJavaVM * vm,const std::string & options,void * reserved)248*795d594fSAndroid Build Coastguard Worker static jint CallNextAgent(StartType start,
249*795d594fSAndroid Build Coastguard Worker ProxyJavaVM* vm,
250*795d594fSAndroid Build Coastguard Worker const std::string& options,
251*795d594fSAndroid Build Coastguard Worker void* reserved) {
252*795d594fSAndroid Build Coastguard Worker // TODO It might be good to set it up so that the library is unloaded even if no jvmtiEnv's are
253*795d594fSAndroid Build Coastguard Worker // created but this isn't expected to be common so we will just not bother.
254*795d594fSAndroid Build Coastguard Worker return ((start == StartType::OnLoad) ? vm->load : vm->attach)(vm, options.c_str(), reserved);
255*795d594fSAndroid Build Coastguard Worker }
256*795d594fSAndroid Build Coastguard Worker
substrOf(const std::string & s,size_t start,size_t end)257*795d594fSAndroid Build Coastguard Worker static std::string substrOf(const std::string& s, size_t start, size_t end) {
258*795d594fSAndroid Build Coastguard Worker if (end == start) {
259*795d594fSAndroid Build Coastguard Worker return "";
260*795d594fSAndroid Build Coastguard Worker } else if (end == std::string::npos) {
261*795d594fSAndroid Build Coastguard Worker end = s.size();
262*795d594fSAndroid Build Coastguard Worker }
263*795d594fSAndroid Build Coastguard Worker return s.substr(start, end - start);
264*795d594fSAndroid Build Coastguard Worker }
265*795d594fSAndroid Build Coastguard Worker
ReadPropMap(const std::string & file)266*795d594fSAndroid Build Coastguard Worker static PropMap* ReadPropMap(const std::string& file) {
267*795d594fSAndroid Build Coastguard Worker std::unique_ptr<PropMap> map(new PropMap);
268*795d594fSAndroid Build Coastguard Worker std::ifstream prop_file(file, std::ios::in);
269*795d594fSAndroid Build Coastguard Worker std::string line;
270*795d594fSAndroid Build Coastguard Worker while (std::getline(prop_file, line)) {
271*795d594fSAndroid Build Coastguard Worker if (line.size() == 0 || line[0] == '#') {
272*795d594fSAndroid Build Coastguard Worker continue;
273*795d594fSAndroid Build Coastguard Worker }
274*795d594fSAndroid Build Coastguard Worker if (line.find('=') == std::string::npos) {
275*795d594fSAndroid Build Coastguard Worker LOG(INFO) << "line: " << line << " didn't have a '='";
276*795d594fSAndroid Build Coastguard Worker return nullptr;
277*795d594fSAndroid Build Coastguard Worker }
278*795d594fSAndroid Build Coastguard Worker std::string prop = substrOf(line, 0, line.find('='));
279*795d594fSAndroid Build Coastguard Worker std::string val = substrOf(line, line.find('=') + 1, std::string::npos);
280*795d594fSAndroid Build Coastguard Worker LOG(INFO) << "Overriding property " << std::quoted(prop) << " new value is "
281*795d594fSAndroid Build Coastguard Worker << std::quoted(val);
282*795d594fSAndroid Build Coastguard Worker map->insert({prop, val});
283*795d594fSAndroid Build Coastguard Worker }
284*795d594fSAndroid Build Coastguard Worker return map.release();
285*795d594fSAndroid Build Coastguard Worker }
286*795d594fSAndroid Build Coastguard Worker
ParseArgs(const std::string & options,std::string * prop_file,std::string * agent_lib,std::string * agent_options)287*795d594fSAndroid Build Coastguard Worker static bool ParseArgs(const std::string& options,
288*795d594fSAndroid Build Coastguard Worker /*out*/std::string* prop_file,
289*795d594fSAndroid Build Coastguard Worker /*out*/std::string* agent_lib,
290*795d594fSAndroid Build Coastguard Worker /*out*/std::string* agent_options) {
291*795d594fSAndroid Build Coastguard Worker if (options.find(',') == std::string::npos) {
292*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "No agent lib in " << options;
293*795d594fSAndroid Build Coastguard Worker return false;
294*795d594fSAndroid Build Coastguard Worker }
295*795d594fSAndroid Build Coastguard Worker *prop_file = substrOf(options, 0, options.find(','));
296*795d594fSAndroid Build Coastguard Worker *agent_lib = substrOf(options, options.find(',') + 1, options.find('='));
297*795d594fSAndroid Build Coastguard Worker if (options.find('=') != std::string::npos) {
298*795d594fSAndroid Build Coastguard Worker *agent_options = substrOf(options, options.find('=') + 1, std::string::npos);
299*795d594fSAndroid Build Coastguard Worker } else {
300*795d594fSAndroid Build Coastguard Worker *agent_options = "";
301*795d594fSAndroid Build Coastguard Worker }
302*795d594fSAndroid Build Coastguard Worker return true;
303*795d594fSAndroid Build Coastguard Worker }
304*795d594fSAndroid Build Coastguard Worker
AgentStart(StartType start,JavaVM * vm,char * options,void * reserved)305*795d594fSAndroid Build Coastguard Worker static jint AgentStart(StartType start, JavaVM* vm, char* options, void* reserved) {
306*795d594fSAndroid Build Coastguard Worker std::string agent_lib;
307*795d594fSAndroid Build Coastguard Worker std::string agent_options;
308*795d594fSAndroid Build Coastguard Worker std::string prop_file;
309*795d594fSAndroid Build Coastguard Worker if (!ParseArgs(options, /*out*/ &prop_file, /*out*/ &agent_lib, /*out*/ &agent_options)) {
310*795d594fSAndroid Build Coastguard Worker return JNI_ERR;
311*795d594fSAndroid Build Coastguard Worker }
312*795d594fSAndroid Build Coastguard Worker // It would be good to not leak these but since they will live for almost the whole program run
313*795d594fSAndroid Build Coastguard Worker // anyway it isn't a huge deal.
314*795d594fSAndroid Build Coastguard Worker PropMap* map = ReadPropMap(prop_file);
315*795d594fSAndroid Build Coastguard Worker if (map == nullptr) {
316*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "unable to read property file at " << std::quoted(prop_file) << "!";
317*795d594fSAndroid Build Coastguard Worker return JNI_ERR;
318*795d594fSAndroid Build Coastguard Worker }
319*795d594fSAndroid Build Coastguard Worker ProxyJavaVM* proxy = new ProxyJavaVM(vm, agent_lib, map);
320*795d594fSAndroid Build Coastguard Worker LOG(INFO) << "Chaining to next agent[" << std::quoted(agent_lib) << "] options=["
321*795d594fSAndroid Build Coastguard Worker << std::quoted(agent_options) << "]";
322*795d594fSAndroid Build Coastguard Worker return CallNextAgent(start, proxy, agent_options, reserved);
323*795d594fSAndroid Build Coastguard Worker }
324*795d594fSAndroid Build Coastguard Worker
325*795d594fSAndroid Build Coastguard Worker // Late attachment (e.g. 'am attach-agent').
Agent_OnAttach(JavaVM * vm,char * options,void * reserved)326*795d594fSAndroid Build Coastguard Worker extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *vm, char* options, void* reserved) {
327*795d594fSAndroid Build Coastguard Worker return AgentStart(StartType::OnAttach, vm, options, reserved);
328*795d594fSAndroid Build Coastguard Worker }
329*795d594fSAndroid Build Coastguard Worker
330*795d594fSAndroid Build Coastguard Worker // Early attachment
331*795d594fSAndroid Build Coastguard Worker // (e.g. 'java -agentpath:/path/to/libwrapagentproperties.so=/path/to/propfile,/path/to/wrapped.so=[ops]').
Agent_OnLoad(JavaVM * jvm,char * options,void * reserved)332*795d594fSAndroid Build Coastguard Worker extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, char* options, void* reserved) {
333*795d594fSAndroid Build Coastguard Worker return AgentStart(StartType::OnLoad, jvm, options, reserved);
334*795d594fSAndroid Build Coastguard Worker }
335*795d594fSAndroid Build Coastguard Worker
Agent_OnUnload(JavaVM * jvm)336*795d594fSAndroid Build Coastguard Worker extern "C" JNIEXPORT void JNICALL Agent_OnUnload(JavaVM* jvm) {
337*795d594fSAndroid Build Coastguard Worker std::lock_guard<std::mutex> lk(unload_mutex);
338*795d594fSAndroid Build Coastguard Worker for (const Unloader& u : unload_functions) {
339*795d594fSAndroid Build Coastguard Worker u.unload(jvm);
340*795d594fSAndroid Build Coastguard Worker // Don't dlclose since some agents expect to still have code loaded after this.
341*795d594fSAndroid Build Coastguard Worker }
342*795d594fSAndroid Build Coastguard Worker unload_functions.clear();
343*795d594fSAndroid Build Coastguard Worker }
344*795d594fSAndroid Build Coastguard Worker
345*795d594fSAndroid Build Coastguard Worker } // namespace wrapagentproperties
346*795d594fSAndroid Build Coastguard Worker
347