diff options
author | 2019-03-11 15:50:24 +0000 | |
---|---|---|
committer | 2019-03-12 00:25:43 +0000 | |
commit | 2d8b8119228bbe4202490b1d5fed6191989d5d83 (patch) | |
tree | 8740119024f873d08d2fc57022d1f5455f1d9ff6 | |
parent | 7cb707ffe41774150cd41283ebb6f9b3b86060ea (diff) |
Revert^2 "Add extension and agent for dumping internal jvmti plugin data."
This reverts commit 9da91d2ee78a32e4bfa5f402f23835652b87023e.
Reason for revert: Fixed issue with previous CL
Test: ./test.py --host
Change-Id: I02a2966439ac45b1843ea28e64b7b30f7282c794
-rw-r--r-- | openjdkjvmti/deopt_manager.cc | 52 | ||||
-rw-r--r-- | openjdkjvmti/deopt_manager.h | 3 | ||||
-rw-r--r-- | openjdkjvmti/ti_dump.cc | 20 | ||||
-rw-r--r-- | openjdkjvmti/ti_dump.h | 2 | ||||
-rw-r--r-- | openjdkjvmti/ti_extension.cc | 15 | ||||
-rw-r--r-- | tools/dump-jvmti-state/Android.bp | 56 | ||||
-rw-r--r-- | tools/dump-jvmti-state/README.md | 27 | ||||
-rw-r--r-- | tools/dump-jvmti-state/dump-jvmti.cc | 115 |
8 files changed, 288 insertions, 2 deletions
diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc index ee77b7bb77..ec29f2cdda 100644 --- a/openjdkjvmti/deopt_manager.cc +++ b/openjdkjvmti/deopt_manager.cc @@ -30,6 +30,8 @@ */ #include <functional> +#include <iosfwd> +#include <mutex> #include "deopt_manager.h" @@ -109,6 +111,53 @@ void DeoptManager::Shutdown() { callbacks->RemoveMethodInspectionCallback(&inspection_callback_); } +void DeoptManager::DumpDeoptInfo(art::Thread* self, std::ostream& stream) { + art::ScopedObjectAccess soa(self); + art::MutexLock mutll(self, *art::Locks::thread_list_lock_); + art::MutexLock mudsl(self, deoptimization_status_lock_); + art::MutexLock mubsl(self, breakpoint_status_lock_); + stream << "Deoptimizer count: " << deopter_count_ << "\n"; + stream << "Global deopt count: " << global_deopt_count_ << "\n"; + stream << "Can perform OSR: " << !set_local_variable_called_.load() << "\n"; + for (const auto& [bp, loc] : this->breakpoint_status_) { + stream << "Breakpoint: " << bp->PrettyMethod() << " @ 0x" << std::hex << loc << "\n"; + } + struct DumpThreadDeoptCount : public art::Closure { + public: + DumpThreadDeoptCount(std::ostream& stream, std::mutex& mu) + : cnt_(0), stream_(stream), mu_(mu) {} + void Run(art::Thread* self) override { + { + std::lock_guard<std::mutex> lg(mu_); + std::string name; + self->GetThreadName(name); + stream_ << "Thread " << name << " (id: " << std::dec << self->GetThreadId() + << ") force interpreter count " << self->ForceInterpreterCount() << "\n"; + } + // Increment this after unlocking the mutex so we won't race its destructor. + cnt_++; + } + + void WaitForCount(size_t threads) { + while (cnt_.load() != threads) { + sched_yield(); + } + } + + private: + std::atomic<size_t> cnt_; + std::ostream& stream_; + std::mutex& mu_; + }; + + std::mutex mu; + DumpThreadDeoptCount dtdc(stream, mu); + auto func = [](art::Thread* thread, void* ctx) { + reinterpret_cast<DumpThreadDeoptCount*>(ctx)->Run(thread); + }; + art::Runtime::Current()->GetThreadList()->ForEach(func, &dtdc); +} + void DeoptManager::FinishSetup() { art::Thread* self = art::Thread::Current(); art::MutexLock mu(self, deoptimization_status_lock_); @@ -366,8 +415,7 @@ jvmtiError DeoptManager::AddDeoptimizeThreadMethods(art::ScopedObjectAccessUnche return err; } // We don't need additional locking here because we hold the Thread_list_lock_. - target->SetForceInterpreterCount(target->ForceInterpreterCount() + 1); - if (target->ForceInterpreterCount() == 1) { + if (target->IncrementForceInterpreterCount() == 1) { struct DeoptClosure : public art::Closure { public: explicit DeoptClosure(DeoptManager* man) : man_(man) {} diff --git a/openjdkjvmti/deopt_manager.h b/openjdkjvmti/deopt_manager.h index 4c4a77412e..73a64be7a4 100644 --- a/openjdkjvmti/deopt_manager.h +++ b/openjdkjvmti/deopt_manager.h @@ -33,6 +33,7 @@ #define ART_OPENJDKJVMTI_DEOPT_MANAGER_H_ #include <atomic> +#include <iosfwd> #include <unordered_map> #include "base/mutex.h" @@ -78,6 +79,8 @@ class DeoptManager { void Setup(); void Shutdown(); + void DumpDeoptInfo(art::Thread* self, std::ostream& stream); + void RemoveDeoptimizationRequester() REQUIRES(!deoptimization_status_lock_, !art::Roles::uninterruptible_); void AddDeoptimizationRequester() REQUIRES(!deoptimization_status_lock_, diff --git a/openjdkjvmti/ti_dump.cc b/openjdkjvmti/ti_dump.cc index c9abb71e4c..caf24fa21c 100644 --- a/openjdkjvmti/ti_dump.cc +++ b/openjdkjvmti/ti_dump.cc @@ -32,9 +32,11 @@ #include "ti_dump.h" #include <limits> +#include <sstream> #include "art_jvmti.h" #include "base/mutex.h" +#include "deopt_manager.h" #include "events-inl.h" #include "runtime_callbacks.h" #include "scoped_thread_state_change-inl.h" @@ -70,4 +72,22 @@ void DumpUtil::Unregister() { art::Runtime::Current()->GetRuntimeCallbacks()->RemoveRuntimeSigQuitCallback(&gDumpCallback); } +jvmtiError DumpUtil::DumpInternalState(jvmtiEnv *jvmti, char **data) { + art::Thread* self = art::Thread::Current(); + if (jvmti == nullptr || self == nullptr) { + return ERR(INVALID_ENVIRONMENT); + } else if (data == nullptr) { + return ERR(NULL_POINTER); + } + + std::stringstream ss; + // TODO Add more stuff on here. + DeoptManager::Get()->DumpDeoptInfo(self, ss); + + jvmtiError err = OK; + JvmtiUniquePtr<char[]> res = CopyString(jvmti, ss.str().c_str(), &err); + *data = res.release(); + return err; +} + } // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_dump.h b/openjdkjvmti/ti_dump.h index 323bf56aef..c382b36736 100644 --- a/openjdkjvmti/ti_dump.h +++ b/openjdkjvmti/ti_dump.h @@ -43,6 +43,8 @@ class DumpUtil { public: static void Register(EventHandler* event_handler); static void Unregister(); + + static jvmtiError DumpInternalState(jvmtiEnv* jvmti, char** data); }; } // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc index 5d398844b2..f12cb0a380 100644 --- a/openjdkjvmti/ti_extension.cc +++ b/openjdkjvmti/ti_extension.cc @@ -38,6 +38,7 @@ #include "ti_allocator.h" #include "ti_class.h" #include "ti_ddms.h" +#include "ti_dump.h" #include "ti_heap.h" #include "ti_logging.h" #include "ti_monitor.h" @@ -312,6 +313,20 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, return error; } + // DumpInternalState + error = add_extension( + reinterpret_cast<jvmtiExtensionFunction>(DumpUtil::DumpInternalState), + "com.android.art.misc.get_plugin_internal_state", + "Gets internal state about the plugin and serializes it to the given msg. " + "There is no particular format to this message beyond being human readable.", + { + { "msg", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_CCHAR, false }, + }, + { ERR(NULL_POINTER) }); + if (error != ERR(NONE)) { + return error; + } + // Copy into output buffer. *extension_count_ptr = ext_vector.size(); diff --git a/tools/dump-jvmti-state/Android.bp b/tools/dump-jvmti-state/Android.bp new file mode 100644 index 0000000000..5c78965b40 --- /dev/null +++ b/tools/dump-jvmti-state/Android.bp @@ -0,0 +1,56 @@ +// +// Copyright (C) 2019 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: "dumpjvmti-defaults", + host_supported: true, + srcs: ["dump-jvmti.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", + ], + header_libs: [ + "libopenjdkjvmti_headers", + ], + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + suffix: "64", + }, + }, + symlink_preferred_arch: true, +} + +art_cc_library { + name: "libdumpjvmti", + defaults: ["dumpjvmti-defaults"], +} + +art_cc_library { + name: "libdumpjvmtid", + defaults: [ + "art_debug_defaults", + "dumpjvmti-defaults", + ], +} diff --git a/tools/dump-jvmti-state/README.md b/tools/dump-jvmti-state/README.md new file mode 100644 index 0000000000..4aabc082ac --- /dev/null +++ b/tools/dump-jvmti-state/README.md @@ -0,0 +1,27 @@ +# dumpjvmti + +dumpjvmti is a JVMTI agent designed for helping debug the working of the openjdkjvmti plugin. It +allows one to use SIGQUIT to dump information about the current JVMTI state to logcat. It does +this by calling the com.android.art.misc.get_plugin_internal_state extension function. + +# Usage +### Build +> `make libdumpjvmti` + +The libraries will be built for 32-bit, 64-bit, host and target. Below examples +assume you want to use the 64-bit version. + +#### ART +> `art -Xplugin:$ANDROID_HOST_OUT/lib64/libopenjdkjvmti.so '-agentpath:libdumpjvmti.so' -cp tmp/java/helloworld.dex -Xint helloworld` +> `kill -3 <pid>` + +* `-Xplugin` and `-agentpath` need to be used, otherwise the agent will fail during init. +* If using `libartd.so`, make sure to use the debug version of jvmti. + +> `adb shell setenforce 0` +> +> `adb push $ANDROID_PRODUCT_OUT/system/lib64/libdumpjvmti.so /data/local/tmp/` +> +> `adb shell am start-activity --attach-agent /data/local/tmp/libdumpjvmti.so some.debuggable.apps/.the.app.MainActivity` +> +> `adb shell kill -3 $(adb shell pidof some.debuggable.apps)`
\ No newline at end of file diff --git a/tools/dump-jvmti-state/dump-jvmti.cc b/tools/dump-jvmti-state/dump-jvmti.cc new file mode 100644 index 0000000000..71a0115999 --- /dev/null +++ b/tools/dump-jvmti-state/dump-jvmti.cc @@ -0,0 +1,115 @@ +// Copyright (C) 2019 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 <jni.h> +#include <jvmti.h> + +namespace dumpjvmti { + +namespace { + +// Special art ti-version number. We will use this as a fallback if we cannot get a regular JVMTI +// env. +static constexpr jint kArtTiVersion = JVMTI_VERSION_1_2 | 0x40000000; + +template <typename T> static void Dealloc(jvmtiEnv* env, T* t) { + env->Deallocate(reinterpret_cast<unsigned char*>(t)); +} + +template <typename T, typename... Rest> static void Dealloc(jvmtiEnv* env, T* t, Rest... rs) { + Dealloc(env, t); + Dealloc(env, rs...); +} + +static void DeallocParams(jvmtiEnv* env, jvmtiParamInfo* params, jint n_params) { + for (jint i = 0; i < n_params; i++) { + Dealloc(env, params[i].name); + } +} + +// The extension function to get the internal data +static jvmtiError (*GetInternalData)(jvmtiEnv* env, unsigned char** data) = nullptr; + +static jint SetupJvmtiEnv(JavaVM* vm, jvmtiEnv** jvmti) { + jint res = 0; + res = vm->GetEnv(reinterpret_cast<void**>(jvmti), JVMTI_VERSION_1_1); + + if (res != JNI_OK || *jvmti == nullptr) { + LOG(ERROR) << "Unable to access JVMTI, error code " << res; + res = vm->GetEnv(reinterpret_cast<void**>(jvmti), kArtTiVersion); + if (res != JNI_OK) { + return res; + } + } + + jvmtiEnv* env = *jvmti; + + // Get the extensions. + jint n_ext = 0; + jvmtiExtensionFunctionInfo* infos = nullptr; + if (env->GetExtensionFunctions(&n_ext, &infos) != JVMTI_ERROR_NONE) { + return JNI_ERR; + } + for (jint i = 0; i < n_ext; i++) { + jvmtiExtensionFunctionInfo* cur_info = &infos[i]; + if (strcmp("com.android.art.misc.get_plugin_internal_state", cur_info->id) == 0) { + GetInternalData = reinterpret_cast<decltype(GetInternalData)>(cur_info->func); + } + // Cleanup the cur_info + DeallocParams(env, cur_info->params, cur_info->param_count); + Dealloc(env, cur_info->id, cur_info->short_description, cur_info->params, cur_info->errors); + } + // Cleanup the array. + Dealloc(env, infos); + return GetInternalData != nullptr ? JNI_OK : JNI_ERR; +} + +static void CbDataDump(jvmtiEnv* jvmti) { + unsigned char* data = nullptr; + if (JVMTI_ERROR_NONE == GetInternalData(jvmti, &data)) { + LOG(INFO) << data; + Dealloc(jvmti, data); + } +} + +} // namespace + +static jint AgentStart(JavaVM* vm, char* options ATTRIBUTE_UNUSED, void* reserved ATTRIBUTE_UNUSED) { + jvmtiEnv* jvmti = nullptr; + if (SetupJvmtiEnv(vm, &jvmti) != JNI_OK) { + LOG(ERROR) << "Could not get JVMTI env or ArtTiEnv!"; + return JNI_ERR; + } + jvmtiEventCallbacks cb{ + .DataDumpRequest = CbDataDump, + }; + jvmti->SetEventCallbacks(&cb, sizeof(cb)); + jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_DATA_DUMP_REQUEST, nullptr); + return JNI_OK; +} + +// Late attachment (e.g. 'am attach-agent'). +extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) { + return AgentStart(vm, options, reserved); +} + +// Early attachment +extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, char* options, void* reserved) { + return AgentStart(jvm, options, reserved); +} + +} // namespace dumpjvmti |