diff options
| -rw-r--r-- | Android.bp | 1 | ||||
| -rw-r--r-- | runtime/jit/profile_compilation_info.cc | 27 | ||||
| -rw-r--r-- | runtime/jit/profile_compilation_info.h | 6 | ||||
| -rw-r--r-- | tools/libjdwp-compat.props | 18 | ||||
| -rwxr-xr-x | tools/run-jdwp-tests.sh | 21 | ||||
| -rw-r--r-- | tools/wrapagentproperties/Android.bp | 66 | ||||
| -rw-r--r-- | tools/wrapagentproperties/README.md | 30 | ||||
| -rw-r--r-- | tools/wrapagentproperties/wrapagentproperties.cc | 346 |
8 files changed, 503 insertions, 12 deletions
diff --git a/Android.bp b/Android.bp index 8678fd06d2..1b66e6fbbd 100644 --- a/Android.bp +++ b/Android.bp @@ -42,4 +42,5 @@ subdirs = [ "tools/cpp-define-generator", "tools/dmtracedump", "tools/titrace", + "tools/wrapagentproperties", ] diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index 57fc4976f7..12fa49ea79 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -143,8 +143,7 @@ bool ProfileCompilationInfo::AddMethodIndex(MethodHotness::Flag flags, const Met if (data == nullptr) { return false; } - data->AddMethod(flags, ref.index); - return true; + return data->AddMethod(flags, ref.index); } bool ProfileCompilationInfo::AddMethodIndex(MethodHotness::Flag flags, @@ -158,8 +157,7 @@ bool ProfileCompilationInfo::AddMethodIndex(MethodHotness::Flag flags, if (data == nullptr) { return false; } - data->AddMethod(flags, method_idx); - return true; + return data->AddMethod(flags, method_idx); } bool ProfileCompilationInfo::AddMethods(const std::vector<ProfileMethodInfo>& methods) { @@ -592,7 +590,14 @@ ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData // This should always be the case since since the cache map is managed by ProfileCompilationInfo. DCHECK_EQ(profile_key, result->profile_key); DCHECK_EQ(profile_index, result->profile_index); - DCHECK_EQ(num_method_ids, result->num_method_ids); + + if (num_method_ids != result->num_method_ids) { + // This should not happen... added to help investigating b/65812889. + LOG(ERROR) << "num_method_ids mismatch for dex " << profile_key + << ", expected=" << num_method_ids + << ", actual=" << result->num_method_ids; + return nullptr; + } return result; } @@ -1342,8 +1347,8 @@ bool ProfileCompilationInfo::AddMethodHotness(const MethodReference& method_ref, DexFileData* dex_data = GetOrAddDexFileData(method_ref.dex_file); if (dex_data != nullptr) { // TODO: Add inline caches. - dex_data->AddMethod(static_cast<MethodHotness::Flag>(hotness.GetFlags()), method_ref.index); - return true; + return dex_data->AddMethod( + static_cast<MethodHotness::Flag>(hotness.GetFlags()), method_ref.index); } return false; } @@ -1727,7 +1732,12 @@ ProfileCompilationInfo::DexFileData::FindOrAddMethod(uint16_t method_index) { } // Mark a method as executed at least once. -void ProfileCompilationInfo::DexFileData::AddMethod(MethodHotness::Flag flags, size_t index) { +bool ProfileCompilationInfo::DexFileData::AddMethod(MethodHotness::Flag flags, size_t index) { + if (index >= num_method_ids) { + LOG(ERROR) << "Invalid method index " << index << ". num_method_ids=" << num_method_ids; + return false; + } + if ((flags & MethodHotness::kFlagStartup) != 0) { method_bitmap.StoreBit(MethodBitIndex(/*startup*/ true, index), /*value*/ true); } @@ -1739,6 +1749,7 @@ void ProfileCompilationInfo::DexFileData::AddMethod(MethodHotness::Flag flags, s index, InlineCacheMap(std::less<uint16_t>(), arena_->Adapter(kArenaAllocProfile))); } + return true; } ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::DexFileData::GetHotnessInfo( diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h index 5c7448fe63..009554c7ca 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/runtime/jit/profile_compilation_info.h @@ -290,7 +290,9 @@ class ProfileCompilationInfo { } for (Iterator it = index_begin; it != index_end; ++it) { DCHECK_LT(*it, data->num_method_ids); - data->AddMethod(flags, *it); + if (!data->AddMethod(flags, *it)) { + return false; + } } return true; } @@ -444,7 +446,7 @@ class ProfileCompilationInfo { } // Mark a method as executed at least once. - void AddMethod(MethodHotness::Flag flags, size_t index); + bool AddMethod(MethodHotness::Flag flags, size_t index); void MergeBitmap(const DexFileData& other) { DCHECK_EQ(bitmap_storage.size(), other.bitmap_storage.size()); diff --git a/tools/libjdwp-compat.props b/tools/libjdwp-compat.props new file mode 100644 index 0000000000..c573b2470b --- /dev/null +++ b/tools/libjdwp-compat.props @@ -0,0 +1,18 @@ +# Copyright 2017 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. +# +# These are properties that are needed for RI jdwp to run. +java.vm.info=mixed mode +sun.boot.class.path= +sun.boot.library.path= diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index 2e59af92f5..d0e35ac905 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -45,6 +45,8 @@ plugin="" debug="no" verbose="no" image="-Ximage:/data/art-test/core.art" +with_jdwp_path="" +agent_wrapper="" vm_args="" # By default, we run the whole JDWP test suite. test="org.apache.harmony.jpda.tests.share.AllTests" @@ -89,6 +91,14 @@ while true; do # We don't care about jit with the RI use_jit=false shift + elif [[ $1 == --agent-wrapper ]]; then + # Remove the --agent-wrapper from the arguments. + args=${args/$1} + shift + agent_wrapper=${agent_wrapper}${1}, + # Remove the argument + args=${args/$1} + shift elif [[ $1 == -Ximage:* ]]; then image="$1" shift @@ -119,8 +129,7 @@ while true; do # Remove the --jdwp-path from the arguments. args=${args/$1} shift - vm_args="${vm_args} --vm-arg -Djpda.settings.debuggeeAgentArgument=\"-agentpath:\"" - vm_args="${vm_args} --vm-arg -Djpda.settings.debuggeeAgentName=$1" + with_jdwp_path=$1 # Remove the path from the arguments. args=${args/$1} shift @@ -140,6 +149,10 @@ done if [[ $mode == "ri" ]]; then using_jack="false" + if [[ "x$with_jdwp_path" != "x" ]]; then + vm_args="${vm_args} --vm-arg -Djpda.settings.debuggeeAgentArgument=-agentpath:${agent_wrapper}" + vm_args="${vm_args} --vm-arg -Djpda.settings.debuggeeAgentName=$with_jdwp_path" + fi if [[ "x$image" != "x" ]]; then echo "Cannot use -Ximage: with --mode=jvm" exit 1 @@ -148,6 +161,10 @@ if [[ $mode == "ri" ]]; then exit 1 fi else + if [[ "x$with_jdwp_path" != "x" ]]; then + vm_args="${vm_args} --vm-arg -Djpda.settings.debuggeeAgentArgument=-agentpath:${agent_wrapper}" + vm_args="${vm_args} --vm-arg -Djpda.settings.debuggeeAgentName=${with_jdwp_path}" + fi vm_args="$vm_args --vm-arg -Xcompiler-option --vm-arg --debuggable" # Make sure the debuggee doesn't clean up what the debugger has generated. art_debugee="$art_debugee --no-clean" diff --git a/tools/wrapagentproperties/Android.bp b/tools/wrapagentproperties/Android.bp new file mode 100644 index 0000000000..c39b81a279 --- /dev/null +++ b/tools/wrapagentproperties/Android.bp @@ -0,0 +1,66 @@ +// +// Copyright (C) 2017 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. +// + +// Build variants {target,host} x {debug,ndebug} x {32,64} + +cc_defaults { + name: "wrapagentproperties-defaults", + host_supported: true, + srcs: ["wrapagentproperties.cc"], + defaults: ["art_defaults"], + + // Note that this tool needs to be built for both 32-bit and 64-bit since it requires + // to be same ISA as what it is attached to. + compile_multilib: "both", + + shared_libs: [ + "libbase" + ], + target: { + android: { + }, + host: { + }, + }, + header_libs: [ + "libopenjdkjvmti_headers", + ], + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + suffix: "64", + }, + }, + symlink_preferred_arch: true, +} + +art_cc_library { + name: "libwrapagentproperties", + defaults: ["wrapagentproperties-defaults"], + shared_libs: [ + ], +} + +art_cc_library { + name: "libwrapagentpropertiesd", + defaults: [ + "art_debug_defaults", + "wrapagentproperties-defaults", + ], + shared_libs: [ ], +} diff --git a/tools/wrapagentproperties/README.md b/tools/wrapagentproperties/README.md new file mode 100644 index 0000000000..d968087cf8 --- /dev/null +++ b/tools/wrapagentproperties/README.md @@ -0,0 +1,30 @@ +# wrapagentproperties + +wrapagentproperties is a JVMTI agent that lets one change the returned values of +an agents GetSystemPropert{y,ies} calls. + +# Usage +### Build +> `make libwrapagentproperties` # or 'make libwrapagentpropertiesd' with debugging checks enabled + +The libraries will be built for 32-bit, 64-bit, host and target. Below examples +assume you want to use the 64-bit version. + +### Command Line +#### ART +> `art -Xplugin:$ANDROID_HOST_OUT/lib64/libopenjdkjvmti.so -agentpath:$ANDROID_HOST_OUT/lib64/libwrapagentproperties.so=/path/to/prop.file,/path/to/agent=agent-args -cp tmp/java/helloworld.dex -Xint helloworld` + +* `-Xplugin` and `-agentpath` need to be used, otherwise libtitrace agent will fail during init. +* If using `libartd.so`, make sure to use the debug version of jvmti. + +### prop file format. + +The property file is a text file containing the values of java properties you +wish to override. The format is property=value on each line. Blank lines and +lines beginning with "#" are ignored. + +#### Example prop file + + # abc.prop + abc.def=123 + def.hij=a big deal diff --git a/tools/wrapagentproperties/wrapagentproperties.cc b/tools/wrapagentproperties/wrapagentproperties.cc new file mode 100644 index 0000000000..dca627046e --- /dev/null +++ b/tools/wrapagentproperties/wrapagentproperties.cc @@ -0,0 +1,346 @@ +// Copyright (C) 2017 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 <android-base/logging.h> +#include <atomic> +#include <dlfcn.h> +#include <iostream> +#include <fstream> +#include <iomanip> +#include <jni.h> +#include <jvmti.h> +#include <unordered_map> +#include <unordered_set> +#include <memory> +#include <mutex> +#include <sstream> +#include <string> +#include <vector> + +namespace wrapagentproperties { + +using PropMap = std::unordered_map<std::string, std::string>; +static constexpr const char* kOnLoad = "Agent_OnLoad"; +static constexpr const char* kOnAttach = "Agent_OnAttach"; +static constexpr const char* kOnUnload= "Agent_OnUnload"; +struct ProxyJavaVM; +using AgentLoadFunction = jint (*)(ProxyJavaVM*, const char*, void*); +using AgentUnloadFunction = jint (*)(JavaVM*); + +// Global namespace. Shared by every usage of this wrapper unfortunately. +// We need to keep track of them to call Agent_OnUnload. +static std::mutex unload_mutex; + +struct Unloader { + AgentUnloadFunction unload; + void* dlclose_handle; +}; +static std::vector<Unloader> unload_functions; + +static jint CreateJvmtiEnv(ProxyJavaVM* vm, void** out_env, jint version); + +struct ProxyJavaVM { + const struct JNIInvokeInterface* functions; + JavaVM* real_vm; + PropMap* map; + void* dlopen_handle; + AgentLoadFunction load; + AgentLoadFunction attach; + + ProxyJavaVM(JavaVM* vm, const std::string& agent_lib, PropMap* map) + : functions(CreateInvokeInterface()), + real_vm(vm), + map(map), + dlopen_handle(dlopen(agent_lib.c_str(), RTLD_LAZY)), + load(nullptr), + attach(nullptr) { + CHECK(dlopen_handle != nullptr) << "unable to open " << agent_lib; + { + std::lock_guard<std::mutex> lk(unload_mutex); + unload_functions.push_back({ + reinterpret_cast<AgentUnloadFunction>(dlsym(dlopen_handle, kOnUnload)), + dlopen_handle + }); + } + attach = reinterpret_cast<AgentLoadFunction>(dlsym(dlopen_handle, kOnAttach)); + load = reinterpret_cast<AgentLoadFunction>(dlsym(dlopen_handle, kOnLoad)); + } + + // TODO Use this to cleanup + static jint WrapDestroyJavaVM(ProxyJavaVM* vm) { + return vm->real_vm->DestroyJavaVM(); + } + static jint WrapAttachCurrentThread(ProxyJavaVM* vm, JNIEnv** env, void* res) { + return vm->real_vm->AttachCurrentThread(env, res); + } + static jint WrapDetachCurrentThread(ProxyJavaVM* vm) { + return vm->real_vm->DetachCurrentThread(); + } + static jint WrapAttachCurrentThreadAsDaemon(ProxyJavaVM* vm, JNIEnv** env, void* res) { + return vm->real_vm->AttachCurrentThreadAsDaemon(env, res); + } + + static jint WrapGetEnv(ProxyJavaVM* vm, void** out_env, jint version) { + switch (version) { + case JVMTI_VERSION: + case JVMTI_VERSION_1: + case JVMTI_VERSION_1_1: + case JVMTI_VERSION_1_2: + return CreateJvmtiEnv(vm, out_env, version); + default: + if ((version & 0x30000000) == 0x30000000) { + LOG(ERROR) << "Version number 0x" << std::hex << version << " looks like a JVMTI " + << "version but it is not one that is recognized. The wrapper might not " + << "function correctly! Continuing anyway."; + } + return vm->real_vm->GetEnv(out_env, version); + } + } + + static JNIInvokeInterface* CreateInvokeInterface() { + JNIInvokeInterface* out = new JNIInvokeInterface; + memset(out, 0, sizeof(JNIInvokeInterface)); + out->DestroyJavaVM = reinterpret_cast<jint (*)(JavaVM*)>(WrapDestroyJavaVM); + out->AttachCurrentThread = + reinterpret_cast<jint(*)(JavaVM*, JNIEnv**, void*)>(WrapAttachCurrentThread); + out->DetachCurrentThread = reinterpret_cast<jint(*)(JavaVM*)>(WrapDetachCurrentThread); + out->GetEnv = reinterpret_cast<jint(*)(JavaVM*, void**, jint)>(WrapGetEnv); + out->AttachCurrentThreadAsDaemon = + reinterpret_cast<jint(*)(JavaVM*, JNIEnv**, void*)>(WrapAttachCurrentThreadAsDaemon); + return out; + } +}; + + +struct ExtraJvmtiInterface : public jvmtiInterface_1_ { + ProxyJavaVM* proxy_vm; + jvmtiInterface_1_ const* original_interface; + + static jvmtiError WrapDisposeEnvironment(jvmtiEnv* env) { + ExtraJvmtiInterface* funcs = reinterpret_cast<ExtraJvmtiInterface*>( + const_cast<jvmtiInterface_1_*>(env->functions)); + jvmtiInterface_1_** out_iface = const_cast<jvmtiInterface_1_**>(&env->functions); + *out_iface = const_cast<jvmtiInterface_1_*>(funcs->original_interface); + funcs->original_interface->Deallocate(env, reinterpret_cast<unsigned char*>(funcs)); + jvmtiError res = (*out_iface)->DisposeEnvironment(env); + return res; + } + + static jvmtiError WrapGetSystemProperty(jvmtiEnv* env, const char* prop, char** out) { + ExtraJvmtiInterface* funcs = reinterpret_cast<ExtraJvmtiInterface*>( + const_cast<jvmtiInterface_1_*>(env->functions)); + if (funcs->proxy_vm->map->find(prop) != funcs->proxy_vm->map->end()) { + std::string str_prop(prop); + const std::string& val = funcs->proxy_vm->map->at(str_prop); + jvmtiError res = env->Allocate(val.size() + 1, reinterpret_cast<unsigned char**>(out)); + if (res != JVMTI_ERROR_NONE) { + return res; + } + strcpy(*out, val.c_str()); + return JVMTI_ERROR_NONE; + } else { + return funcs->original_interface->GetSystemProperty(env, prop, out); + } + } + + static jvmtiError WrapGetSystemProperties(jvmtiEnv* env, jint* cnt, char*** prop_ptr) { + ExtraJvmtiInterface* funcs = reinterpret_cast<ExtraJvmtiInterface*>( + const_cast<jvmtiInterface_1_*>(env->functions)); + jint init_cnt; + char** init_prop_ptr; + jvmtiError res = funcs->original_interface->GetSystemProperties(env, &init_cnt, &init_prop_ptr); + if (res != JVMTI_ERROR_NONE) { + return res; + } + std::unordered_set<std::string> all_props; + for (const auto& p : *funcs->proxy_vm->map) { + all_props.insert(p.first); + } + for (jint i = 0; i < init_cnt; i++) { + all_props.insert(init_prop_ptr[i]); + env->Deallocate(reinterpret_cast<unsigned char*>(init_prop_ptr[i])); + } + env->Deallocate(reinterpret_cast<unsigned char*>(init_prop_ptr)); + *cnt = all_props.size(); + res = env->Allocate(all_props.size() * sizeof(char*), + reinterpret_cast<unsigned char**>(prop_ptr)); + if (res != JVMTI_ERROR_NONE) { + return res; + } + char** out_prop_ptr = *prop_ptr; + jint i = 0; + for (const std::string& p : all_props) { + res = env->Allocate(p.size() + 1, reinterpret_cast<unsigned char**>(&out_prop_ptr[i])); + if (res != JVMTI_ERROR_NONE) { + return res; + } + strcpy(out_prop_ptr[i], p.c_str()); + i++; + } + CHECK_EQ(i, *cnt); + return JVMTI_ERROR_NONE; + } + + static jvmtiError WrapSetSystemProperty(jvmtiEnv* env, const char* prop, const char* val) { + ExtraJvmtiInterface* funcs = reinterpret_cast<ExtraJvmtiInterface*>( + const_cast<jvmtiInterface_1_*>(env->functions)); + jvmtiError res = funcs->original_interface->SetSystemProperty(env, prop, val); + if (res != JVMTI_ERROR_NONE) { + return res; + } + if (funcs->proxy_vm->map->find(prop) != funcs->proxy_vm->map->end()) { + funcs->proxy_vm->map->at(prop) = val; + } + return JVMTI_ERROR_NONE; + } + + // TODO It would be way better to actually set up a full proxy like we did for JavaVM but the + // number of functions makes it not worth it. + static jint SetupProxyJvmtiEnv(ProxyJavaVM* vm, jvmtiEnv* real_env) { + ExtraJvmtiInterface* new_iface = nullptr; + if (JVMTI_ERROR_NONE != real_env->Allocate(sizeof(ExtraJvmtiInterface), + reinterpret_cast<unsigned char**>(&new_iface))) { + LOG(ERROR) << "Could not allocate extra space for new jvmti interface struct"; + return JNI_ERR; + } + memcpy(new_iface, real_env->functions, sizeof(jvmtiInterface_1_)); + new_iface->proxy_vm = vm; + new_iface->original_interface = real_env->functions; + + // Replace these functions with the new ones. + new_iface->DisposeEnvironment = WrapDisposeEnvironment; + new_iface->GetSystemProperty = WrapGetSystemProperty; + new_iface->GetSystemProperties = WrapGetSystemProperties; + new_iface->SetSystemProperty = WrapSetSystemProperty; + + // Replace the functions table with our new one with replaced functions. + jvmtiInterface_1_** out_iface = const_cast<jvmtiInterface_1_**>(&real_env->functions); + *out_iface = new_iface; + return JNI_OK; + } +}; + +static jint CreateJvmtiEnv(ProxyJavaVM* vm, void** out_env, jint version) { + jint res = vm->real_vm->GetEnv(out_env, version); + if (res != JNI_OK) { + LOG(WARNING) << "Could not create jvmtiEnv to proxy!"; + return res; + } + return ExtraJvmtiInterface::SetupProxyJvmtiEnv(vm, reinterpret_cast<jvmtiEnv*>(*out_env)); +} + +enum class StartType { + OnAttach, OnLoad, +}; + +static jint CallNextAgent(StartType start, + ProxyJavaVM* vm, + std::string options, + void* reserved) { + // TODO It might be good to set it up so that the library is unloaded even if no jvmtiEnv's are + // created but this isn't expected to be common so we will just not bother. + return ((start == StartType::OnLoad) ? vm->load : vm->attach)(vm, options.c_str(), reserved); +} + +static std::string substrOf(const std::string& s, size_t start, size_t end) { + if (end == start) { + return ""; + } else if (end == std::string::npos) { + end = s.size(); + } + return s.substr(start, end - start); +} + +static PropMap* ReadPropMap(const std::string& file) { + std::unique_ptr<PropMap> map(new PropMap); + std::ifstream prop_file(file, std::ios::in); + std::string line; + while (std::getline(prop_file, line)) { + if (line.size() == 0 || line[0] == '#') { + continue; + } + if (line.find('=') == std::string::npos) { + LOG(INFO) << "line: " << line << " didn't have a '='"; + return nullptr; + } + std::string prop = substrOf(line, 0, line.find('=')); + std::string val = substrOf(line, line.find('=') + 1, std::string::npos); + LOG(INFO) << "Overriding property " << std::quoted(prop) << " new value is " + << std::quoted(val); + map->insert({prop, val}); + } + return map.release(); +} + +static bool ParseArgs(const std::string& options, + /*out*/std::string* prop_file, + /*out*/std::string* agent_lib, + /*out*/std::string* agent_options) { + if (options.find(',') == std::string::npos) { + LOG(ERROR) << "No agent lib in " << options; + return false; + } + *prop_file = substrOf(options, 0, options.find(',')); + *agent_lib = substrOf(options, options.find(',') + 1, options.find('=')); + if (options.find('=') != std::string::npos) { + *agent_options = substrOf(options, options.find('=') + 1, std::string::npos); + } else { + *agent_options = ""; + } + return true; +} + +static jint AgentStart(StartType start, JavaVM* vm, char* options, void* reserved) { + std::string agent_lib; + std::string agent_options; + std::string prop_file; + if (!ParseArgs(options, /*out*/ &prop_file, /*out*/ &agent_lib, /*out*/ &agent_options)) { + return JNI_ERR; + } + // It would be good to not leak these but since they will live for almost the whole program run + // anyway it isn't a huge deal. + PropMap* map = ReadPropMap(prop_file); + if (map == nullptr) { + LOG(ERROR) << "unable to read property file at " << std::quoted(prop_file) << "!"; + return JNI_ERR; + } + ProxyJavaVM* proxy = new ProxyJavaVM(vm, agent_lib, map); + LOG(INFO) << "Chaining to next agent[" << std::quoted(agent_lib) << "] options=[" + << std::quoted(agent_options) << "]"; + return CallNextAgent(start, proxy, agent_options, reserved); +} + +// Late attachment (e.g. 'am attach-agent'). +extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *vm, char* options, void* reserved) { + return AgentStart(StartType::OnAttach, vm, options, reserved); +} + +// Early attachment +// (e.g. 'java -agentpath:/path/to/libwrapagentproperties.so=/path/to/propfile,/path/to/wrapped.so=[ops]'). +extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, char* options, void* reserved) { + return AgentStart(StartType::OnLoad, jvm, options, reserved); +} + +extern "C" JNIEXPORT void JNICALL Agent_OnUnload(JavaVM* jvm) { + std::lock_guard<std::mutex> lk(unload_mutex); + for (const Unloader& u : unload_functions) { + u.unload(jvm); + dlclose(u.dlclose_handle); + } + unload_functions.clear(); +} + +} // namespace wrapagentproperties + |