diff options
Diffstat (limited to 'openjdkjvmti')
| -rw-r--r-- | openjdkjvmti/Android.bp | 1 | ||||
| -rw-r--r-- | openjdkjvmti/OpenjdkJvmTi.cc | 19 | ||||
| -rw-r--r-- | openjdkjvmti/art_jvmti.h | 2 | ||||
| -rw-r--r-- | openjdkjvmti/events-inl.h | 11 | ||||
| -rw-r--r-- | openjdkjvmti/events.cc | 75 | ||||
| -rw-r--r-- | openjdkjvmti/events.h | 30 | ||||
| -rw-r--r-- | openjdkjvmti/include/CPPLINT.cfg | 18 | ||||
| -rw-r--r-- | openjdkjvmti/jvmti_allocator.h | 4 | ||||
| -rw-r--r-- | openjdkjvmti/ti_class.cc | 2 | ||||
| -rw-r--r-- | openjdkjvmti/ti_ddms.cc | 85 | ||||
| -rw-r--r-- | openjdkjvmti/ti_ddms.h | 53 | ||||
| -rw-r--r-- | openjdkjvmti/ti_extension.cc | 167 | ||||
| -rw-r--r-- | openjdkjvmti/ti_extension.h | 5 | ||||
| -rw-r--r-- | openjdkjvmti/ti_monitor.cc | 6 |
14 files changed, 443 insertions, 35 deletions
diff --git a/openjdkjvmti/Android.bp b/openjdkjvmti/Android.bp index aef4dfc201..9ba7068176 100644 --- a/openjdkjvmti/Android.bp +++ b/openjdkjvmti/Android.bp @@ -34,6 +34,7 @@ cc_defaults { "ti_class.cc", "ti_class_definition.cc", "ti_class_loader.cc", + "ti_ddms.cc", "ti_dump.cc", "ti_extension.cc", "ti_field.cc", diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc index 481492402b..62f723dec1 100644 --- a/openjdkjvmti/OpenjdkJvmTi.cc +++ b/openjdkjvmti/OpenjdkJvmTi.cc @@ -1014,14 +1014,21 @@ class JvmtiFunctions { return ERR(NONE); } - std::unique_ptr<jvmtiEventCallbacks> tmp(new jvmtiEventCallbacks()); - memset(tmp.get(), 0, sizeof(jvmtiEventCallbacks)); + // Lock the event_info_mutex_ while we replace the callbacks. + ArtJvmTiEnv* art_env = ArtJvmTiEnv::AsArtJvmTiEnv(env); + art::WriterMutexLock lk(art::Thread::Current(), art_env->event_info_mutex_); + std::unique_ptr<ArtJvmtiEventCallbacks> tmp(new ArtJvmtiEventCallbacks()); + // Copy over the extension events. + tmp->CopyExtensionsFrom(art_env->event_callbacks.get()); + // Never overwrite the extension events. size_t copy_size = std::min(sizeof(jvmtiEventCallbacks), static_cast<size_t>(size_of_callbacks)); copy_size = art::RoundDown(copy_size, sizeof(void*)); + // Copy non-extension events. memcpy(tmp.get(), callbacks, copy_size); - ArtJvmTiEnv::AsArtJvmTiEnv(env)->event_callbacks = std::move(tmp); + // replace the event table. + art_env->event_callbacks = std::move(tmp); return ERR(NONE); } @@ -1077,8 +1084,10 @@ class JvmtiFunctions { jint extension_event_index, jvmtiExtensionEvent callback) { ENSURE_VALID_ENV(env); - // We do not have any extension events, so any call is illegal. - return ExtensionUtil::SetExtensionEventCallback(env, extension_event_index, callback); + return ExtensionUtil::SetExtensionEventCallback(env, + extension_event_index, + callback, + &gEventHandler); } static jvmtiError GetPotentialCapabilities(jvmtiEnv* env, jvmtiCapabilities* capabilities_ptr) { diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h index 97801e09aa..682b82b5cd 100644 --- a/openjdkjvmti/art_jvmti.h +++ b/openjdkjvmti/art_jvmti.h @@ -68,7 +68,7 @@ struct ArtJvmTiEnv : public jvmtiEnv { jvmtiCapabilities capabilities; EventMasks event_masks; - std::unique_ptr<jvmtiEventCallbacks> event_callbacks; + std::unique_ptr<ArtJvmtiEventCallbacks> event_callbacks; // Tagging is specific to the jvmtiEnv. std::unique_ptr<ObjectTagTable> object_tag_table; diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h index 7f77f90862..5344e0fbde 100644 --- a/openjdkjvmti/events-inl.h +++ b/openjdkjvmti/events-inl.h @@ -80,16 +80,17 @@ namespace impl { fn(GarbageCollectionStart, ArtJvmtiEvent::kGarbageCollectionStart) \ fn(GarbageCollectionFinish, ArtJvmtiEvent::kGarbageCollectionFinish) \ fn(ObjectFree, ArtJvmtiEvent::kObjectFree) \ - fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc) + fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc) \ + fn(DdmPublishChunk, ArtJvmtiEvent::kDdmPublishChunk) template <ArtJvmtiEvent kEvent> struct EventFnType { }; -#define EVENT_FN_TYPE(name, enum_name) \ -template <> \ -struct EventFnType<enum_name> { \ - using type = decltype(jvmtiEventCallbacks().name); \ +#define EVENT_FN_TYPE(name, enum_name) \ +template <> \ +struct EventFnType<enum_name> { \ + using type = decltype(ArtJvmtiEventCallbacks().name); \ }; FORALL_EVENT_TYPES(EVENT_FN_TYPE) diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index 6a64441a4a..d1d606de48 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -60,6 +60,45 @@ namespace openjdkjvmti { +void ArtJvmtiEventCallbacks::CopyExtensionsFrom(const ArtJvmtiEventCallbacks* cb) { + if (art::kIsDebugBuild) { + ArtJvmtiEventCallbacks clean; + DCHECK_EQ(memcmp(&clean, this, sizeof(clean)), 0) + << "CopyExtensionsFrom called with initialized eventsCallbacks!"; + } + if (cb != nullptr) { + memcpy(this, cb, sizeof(*this)); + } else { + memset(this, 0, sizeof(*this)); + } +} + +jvmtiError ArtJvmtiEventCallbacks::Set(jint index, jvmtiExtensionEvent cb) { + switch (index) { + case static_cast<jint>(ArtJvmtiEvent::kDdmPublishChunk): + DdmPublishChunk = reinterpret_cast<ArtJvmtiEventDdmPublishChunk>(cb); + return OK; + default: + return ERR(ILLEGAL_ARGUMENT); + } +} + + +bool IsExtensionEvent(jint e) { + return e >= static_cast<jint>(ArtJvmtiEvent::kMinEventTypeVal) && + e <= static_cast<jint>(ArtJvmtiEvent::kMaxEventTypeVal) && + IsExtensionEvent(static_cast<ArtJvmtiEvent>(e)); +} + +bool IsExtensionEvent(ArtJvmtiEvent e) { + switch (e) { + case ArtJvmtiEvent::kDdmPublishChunk: + return true; + default: + return false; + } +} + bool EventMasks::IsEnabledAnywhere(ArtJvmtiEvent event) { return global_event_mask.Test(event) || unioned_thread_event_mask.Test(event); } @@ -213,6 +252,38 @@ static void RunEventCallback(EventHandler* handler, args...); } +static void SetupDdmTracking(art::DdmCallback* listener, bool enable) { + art::ScopedObjectAccess soa(art::Thread::Current()); + if (enable) { + art::Runtime::Current()->GetRuntimeCallbacks()->AddDdmCallback(listener); + } else { + art::Runtime::Current()->GetRuntimeCallbacks()->RemoveDdmCallback(listener); + } +} + +class JvmtiDdmChunkListener : public art::DdmCallback { + public: + explicit JvmtiDdmChunkListener(EventHandler* handler) : handler_(handler) {} + + void DdmPublishChunk(uint32_t type, const art::ArrayRef<const uint8_t>& data) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kDdmPublishChunk)) { + art::Thread* self = art::Thread::Current(); + handler_->DispatchEvent<ArtJvmtiEvent::kDdmPublishChunk>( + self, + static_cast<JNIEnv*>(self->GetJniEnv()), + static_cast<jint>(type), + static_cast<jint>(data.size()), + reinterpret_cast<const jbyte*>(data.data())); + } + } + + private: + EventHandler* handler_; + + DISALLOW_COPY_AND_ASSIGN(JvmtiDdmChunkListener); +}; + class JvmtiAllocationListener : public art::gc::AllocationListener { public: explicit JvmtiAllocationListener(EventHandler* handler) : handler_(handler) {} @@ -924,6 +995,9 @@ bool EventHandler::OtherMonitorEventsEnabledAnywhere(ArtJvmtiEvent event) { // Handle special work for the given event type, if necessary. void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { switch (event) { + case ArtJvmtiEvent::kDdmPublishChunk: + SetupDdmTracking(ddm_listener_.get(), enable); + return; case ArtJvmtiEvent::kVmObjectAlloc: SetupObjectAllocationTracking(alloc_listener_.get(), enable); return; @@ -1104,6 +1178,7 @@ void EventHandler::Shutdown() { EventHandler::EventHandler() { alloc_listener_.reset(new JvmtiAllocationListener(this)); + ddm_listener_.reset(new JvmtiDdmChunkListener(this)); gc_pause_listener_.reset(new JvmtiGcPauseListener(this)); method_trace_listener_.reset(new JvmtiMethodTraceListener(this)); monitor_listener_.reset(new JvmtiMonitorListener(this)); diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h index aed24e59f3..a99ed7b212 100644 --- a/openjdkjvmti/events.h +++ b/openjdkjvmti/events.h @@ -28,13 +28,14 @@ namespace openjdkjvmti { struct ArtJvmTiEnv; class JvmtiAllocationListener; +class JvmtiDdmChunkListener; class JvmtiGcPauseListener; class JvmtiMethodTraceListener; class JvmtiMonitorListener; // an enum for ArtEvents. This differs from the JVMTI events only in that we distinguish between // retransformation capable and incapable loading -enum class ArtJvmtiEvent { +enum class ArtJvmtiEvent : jint { kMinEventTypeVal = JVMTI_MIN_EVENT_TYPE_VAL, kVmInit = JVMTI_EVENT_VM_INIT, kVmDeath = JVMTI_EVENT_VM_DEATH, @@ -68,9 +69,33 @@ enum class ArtJvmtiEvent { kObjectFree = JVMTI_EVENT_OBJECT_FREE, kVmObjectAlloc = JVMTI_EVENT_VM_OBJECT_ALLOC, kClassFileLoadHookRetransformable = JVMTI_MAX_EVENT_TYPE_VAL + 1, - kMaxEventTypeVal = kClassFileLoadHookRetransformable, + kDdmPublishChunk = JVMTI_MAX_EVENT_TYPE_VAL + 2, + kMaxEventTypeVal = kDdmPublishChunk, }; +using ArtJvmtiEventDdmPublishChunk = void (*)(jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jint data_type, + jint data_len, + const jbyte* data); + +struct ArtJvmtiEventCallbacks : jvmtiEventCallbacks { + ArtJvmtiEventCallbacks() : DdmPublishChunk(nullptr) { + memset(this, 0, sizeof(jvmtiEventCallbacks)); + } + + // Copies extension functions from other callback struct if it exists. There must not have been + // any modifications to this struct when it is called. + void CopyExtensionsFrom(const ArtJvmtiEventCallbacks* cb); + + jvmtiError Set(jint index, jvmtiExtensionEvent cb); + + ArtJvmtiEventDdmPublishChunk DdmPublishChunk; +}; + +bool IsExtensionEvent(jint e); +bool IsExtensionEvent(ArtJvmtiEvent e); + // Convert a jvmtiEvent into a ArtJvmtiEvent ALWAYS_INLINE static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e); @@ -245,6 +270,7 @@ class EventHandler { EventMask global_mask; std::unique_ptr<JvmtiAllocationListener> alloc_listener_; + std::unique_ptr<JvmtiDdmChunkListener> ddm_listener_; std::unique_ptr<JvmtiGcPauseListener> gc_pause_listener_; std::unique_ptr<JvmtiMethodTraceListener> method_trace_listener_; std::unique_ptr<JvmtiMonitorListener> monitor_listener_; diff --git a/openjdkjvmti/include/CPPLINT.cfg b/openjdkjvmti/include/CPPLINT.cfg new file mode 100644 index 0000000000..3e717a697f --- /dev/null +++ b/openjdkjvmti/include/CPPLINT.cfg @@ -0,0 +1,18 @@ +# +# 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. +# + +# External headers, not subject to our lint rules. +exclude_files=^jvmti[.]h$ diff --git a/openjdkjvmti/jvmti_allocator.h b/openjdkjvmti/jvmti_allocator.h index e6cbc85170..11af7b67e7 100644 --- a/openjdkjvmti/jvmti_allocator.h +++ b/openjdkjvmti/jvmti_allocator.h @@ -58,7 +58,7 @@ class JvmtiAllocator<void> { JvmtiAllocator() : env_(nullptr) {} template <typename U> - JvmtiAllocator(const JvmtiAllocator<U>& other) // NOLINT, implicit + JvmtiAllocator(const JvmtiAllocator<U>& other) : env_(other.env_) {} JvmtiAllocator(const JvmtiAllocator& other) = default; @@ -95,7 +95,7 @@ class JvmtiAllocator { JvmtiAllocator() : env_(nullptr) {} template <typename U> - JvmtiAllocator(const JvmtiAllocator<U>& other) // NOLINT, implicit + JvmtiAllocator(const JvmtiAllocator<U>& other) : env_(other.env_) {} JvmtiAllocator(const JvmtiAllocator& other) = default; diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc index 2c5e5f9ccf..e69c78bab1 100644 --- a/openjdkjvmti/ti_class.cc +++ b/openjdkjvmti/ti_class.cc @@ -33,7 +33,7 @@ #include "android-base/stringprintf.h" -#include <mutex> // NOLINT [build/c++11] [5] +#include <mutex> #include <unordered_set> #include "art_jvmti.h" diff --git a/openjdkjvmti/ti_ddms.cc b/openjdkjvmti/ti_ddms.cc new file mode 100644 index 0000000000..500a453f78 --- /dev/null +++ b/openjdkjvmti/ti_ddms.cc @@ -0,0 +1,85 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include <functional> +#include <vector> + +#include "ti_ddms.h" + +#include "art_jvmti.h" +#include "base/array_ref.h" +#include "debugger.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-inl.h" + +namespace openjdkjvmti { + +jvmtiError DDMSUtil::HandleChunk(jvmtiEnv* env, + jint type_in, + jint length_in, + const jbyte* data_in, + /*out*/jint* type_out, + /*out*/jint* data_length_out, + /*out*/jbyte** data_out) { + constexpr uint32_t kDdmHeaderSize = sizeof(uint32_t) * 2; + if (env == nullptr || data_in == nullptr || data_out == nullptr || data_length_out == nullptr) { + return ERR(NULL_POINTER); + } else if (length_in < static_cast<jint>(kDdmHeaderSize)) { + // need to get type and length at least. + return ERR(ILLEGAL_ARGUMENT); + } + + art::Thread* self = art::Thread::Current(); + art::ScopedThreadStateChange(self, art::ThreadState::kNative); + + art::ArrayRef<const jbyte> data_arr(data_in, length_in); + std::vector<uint8_t> out_data; + if (!art::Dbg::DdmHandleChunk(self->GetJniEnv(), + type_in, + data_arr, + /*out*/reinterpret_cast<uint32_t*>(type_out), + /*out*/&out_data)) { + LOG(WARNING) << "Something went wrong with handling the ddm chunk."; + return ERR(INTERNAL); + } else { + jvmtiError error = OK; + JvmtiUniquePtr<jbyte[]> ret = AllocJvmtiUniquePtr<jbyte[]>(env, out_data.size(), &error); + if (error != OK) { + return error; + } + memcpy(ret.get(), out_data.data(), out_data.size()); + *data_out = ret.release(); + *data_length_out = static_cast<jint>(out_data.size()); + return OK; + } +} + +} // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_ddms.h b/openjdkjvmti/ti_ddms.h new file mode 100644 index 0000000000..1ea7548607 --- /dev/null +++ b/openjdkjvmti/ti_ddms.h @@ -0,0 +1,53 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_OPENJDKJVMTI_TI_DDMS_H_ +#define ART_OPENJDKJVMTI_TI_DDMS_H_ + +#include "jni.h" +#include "jvmti.h" + +namespace openjdkjvmti { + +class DDMSUtil { + public: + static jvmtiError HandleChunk(jvmtiEnv* env, + jint type_in, + jint length_in, + const jbyte* data_in, + /*out*/ jint* type_out, + /*out*/ jint* data_length_out, + /*out*/ jbyte** data_out); +}; + +} // namespace openjdkjvmti + +#endif // ART_OPENJDKJVMTI_TI_DDMS_H_ diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc index fbed9640a0..afd0723d0f 100644 --- a/openjdkjvmti/ti_extension.cc +++ b/openjdkjvmti/ti_extension.cc @@ -34,8 +34,11 @@ #include "ti_extension.h" #include "art_jvmti.h" +#include "events.h" #include "ti_allocator.h" +#include "ti_ddms.h" #include "ti_heap.h" +#include "thread-inl.h" namespace openjdkjvmti { @@ -51,7 +54,7 @@ struct CParamInfo { JvmtiUniquePtr<char[]> param_name = CopyString(env, name, err); char* name_ptr = param_name.get(); char_buffers->push_back(std::move(param_name)); - return jvmtiParamInfo { name_ptr, kind, base_type, null_ok }; // NOLINT [whitespace/braces] [4] + return jvmtiParamInfo{ name_ptr, kind, base_type, null_ok }; } }; @@ -143,7 +146,7 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, "com.android.art.heap.get_object_heap_id", "Retrieve the heap id of the the object tagged with the given argument. An " "arbitrary object is chosen if multiple objects exist with the same tag.", - { // NOLINT [whitespace/braces] [4] + { { "tag", JVMTI_KIND_IN, JVMTI_TYPE_JLONG, false}, { "heap_id", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false} }, @@ -156,7 +159,7 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, reinterpret_cast<jvmtiExtensionFunction>(HeapExtensions::GetHeapName), "com.android.art.heap.get_heap_name", "Retrieve the name of the heap with the given id.", - { // NOLINT [whitespace/braces] [4] + { { "heap_id", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false}, { "heap_name", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_CCHAR, false} }, @@ -172,13 +175,13 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, " except for additionally passing the heap id of the current object. The jvmtiHeapCallbacks" " structure is reused, with the callbacks field overloaded to a signature of " "jint (*)(jlong, jlong, jlong*, jint length, void*, jint).", - { // NOLINT [whitespace/braces] [4] + { { "heap_filter", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false}, { "klass", JVMTI_KIND_IN, JVMTI_TYPE_JCLASS, true}, { "callbacks", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CVOID, false}, { "user_data", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CVOID, true} }, - { // NOLINT [whitespace/braces] [4] + { ERR(MUST_POSSESS_CAPABILITY), ERR(INVALID_CLASS), ERR(NULL_POINTER), @@ -194,7 +197,7 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, " 'Allocate' jvmti function. This does not include any memory that has been deallocated" " through the 'Deallocate' function. This number is approximate and might not correspond" " exactly to the sum of the sizes of all not freed allocations.", - { // NOLINT [whitespace/braces] [4] + { { "currently_allocated", JVMTI_KIND_OUT, JVMTI_TYPE_JLONG, false}, }, { ERR(NULL_POINTER) }); @@ -202,6 +205,27 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, return error; } + // DDMS extension + error = add_extension( + reinterpret_cast<jvmtiExtensionFunction>(DDMSUtil::HandleChunk), + "com.android.art.internal.ddm.process_chunk", + "Handles a single ddms chunk request and returns a response. The reply data is in the ddms" + " chunk format. It returns the processed chunk. This is provided for backwards compatibility" + " reasons only. Agents should avoid making use of this extension when possible and instead" + " use the other JVMTI entrypoints explicitly.", + { + { "type_in", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, + { "length_in", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, + { "data_in", JVMTI_KIND_IN_BUF, JVMTI_TYPE_JBYTE, false }, + { "type_out", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false }, + { "data_len_out", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false }, + { "data_out", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_JBYTE, false } + }, + { ERR(NULL_POINTER), ERR(ILLEGAL_ARGUMENT), ERR(OUT_OF_MEMORY) }); + if (error != ERR(NONE)) { + return error; + } + // Copy into output buffer. *extension_count_ptr = ext_vector.size(); @@ -230,20 +254,133 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, } -jvmtiError ExtensionUtil::GetExtensionEvents(jvmtiEnv* env ATTRIBUTE_UNUSED, +jvmtiError ExtensionUtil::GetExtensionEvents(jvmtiEnv* env, jint* extension_count_ptr, jvmtiExtensionEventInfo** extensions) { - // We don't have any extension events at the moment. - *extension_count_ptr = 0; - *extensions = nullptr; + std::vector<jvmtiExtensionEventInfo> ext_vector; + + // Holders for allocated values. + std::vector<JvmtiUniquePtr<char[]>> char_buffers; + std::vector<JvmtiUniquePtr<jvmtiParamInfo[]>> param_buffers; + + auto add_extension = [&](ArtJvmtiEvent extension_event_index, + const char* id, + const char* short_description, + const std::vector<CParamInfo>& params) { + DCHECK(IsExtensionEvent(extension_event_index)); + jvmtiExtensionEventInfo event_info; + jvmtiError error; + + event_info.extension_event_index = static_cast<jint>(extension_event_index); + + JvmtiUniquePtr<char[]> id_ptr = CopyString(env, id, &error); + if (id_ptr == nullptr) { + return error; + } + event_info.id = id_ptr.get(); + char_buffers.push_back(std::move(id_ptr)); + + JvmtiUniquePtr<char[]> descr = CopyString(env, short_description, &error); + if (descr == nullptr) { + return error; + } + event_info.short_description = descr.get(); + char_buffers.push_back(std::move(descr)); + + event_info.param_count = params.size(); + if (!params.empty()) { + JvmtiUniquePtr<jvmtiParamInfo[]> params_ptr = + AllocJvmtiUniquePtr<jvmtiParamInfo[]>(env, params.size(), &error); + if (params_ptr == nullptr) { + return error; + } + event_info.params = params_ptr.get(); + param_buffers.push_back(std::move(params_ptr)); + + for (jint i = 0; i != event_info.param_count; ++i) { + event_info.params[i] = params[i].ToParamInfo(env, &char_buffers, &error); + if (error != OK) { + return error; + } + } + } else { + event_info.params = nullptr; + } + + ext_vector.push_back(event_info); + + return ERR(NONE); + }; + + jvmtiError error; + error = add_extension( + ArtJvmtiEvent::kDdmPublishChunk, + "com.android.art.internal.ddm.publish_chunk", + "Called when there is new ddms information that the agent or other clients can use. The" + " agent is given the 'type' of the ddms chunk and a 'data_size' byte-buffer in 'data'." + " The 'data' pointer is only valid for the duration of the publish_chunk event. The agent" + " is responsible for interpreting the information present in the 'data' buffer. This is" + " provided for backwards-compatibility support only. Agents should prefer to use relevant" + " JVMTI events and functions above listening for this event.", + { + { "jni_env", JVMTI_KIND_IN_PTR, JVMTI_TYPE_JNIENV, false }, + { "type", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, + { "data_size", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, + { "data", JVMTI_KIND_IN_BUF, JVMTI_TYPE_JBYTE, false }, + }); + if (error != OK) { + return error; + } + + // Copy into output buffer. + + *extension_count_ptr = ext_vector.size(); + JvmtiUniquePtr<jvmtiExtensionEventInfo[]> out_data = + AllocJvmtiUniquePtr<jvmtiExtensionEventInfo[]>(env, ext_vector.size(), &error); + if (out_data == nullptr) { + return error; + } + memcpy(out_data.get(), + ext_vector.data(), + ext_vector.size() * sizeof(jvmtiExtensionEventInfo)); + *extensions = out_data.release(); + + // Release all the buffer holders, we're OK now. + for (auto& holder : char_buffers) { + holder.release(); + } + for (auto& holder : param_buffers) { + holder.release(); + } + return OK; } -jvmtiError ExtensionUtil::SetExtensionEventCallback(jvmtiEnv* env ATTRIBUTE_UNUSED, - jint extension_event_index ATTRIBUTE_UNUSED, - jvmtiExtensionEvent callback ATTRIBUTE_UNUSED) { - // We do not have any extension events, so any call is illegal. - return ERR(ILLEGAL_ARGUMENT); +jvmtiError ExtensionUtil::SetExtensionEventCallback(jvmtiEnv* env, + jint extension_event_index, + jvmtiExtensionEvent callback, + EventHandler* event_handler) { + if (!IsExtensionEvent(extension_event_index)) { + return ERR(ILLEGAL_ARGUMENT); + } + ArtJvmTiEnv* art_env = ArtJvmTiEnv::AsArtJvmTiEnv(env); + jvmtiEventMode mode = callback == nullptr ? JVMTI_DISABLE : JVMTI_ENABLE; + // Lock the event_info_mutex_ while we set the event to make sure it isn't lost by a concurrent + // change to the normal callbacks. + { + art::WriterMutexLock lk(art::Thread::Current(), art_env->event_info_mutex_); + if (art_env->event_callbacks.get() == nullptr) { + art_env->event_callbacks.reset(new ArtJvmtiEventCallbacks()); + } + jvmtiError err = art_env->event_callbacks->Set(extension_event_index, callback); + if (err != OK) { + return err; + } + } + return event_handler->SetEvent(art_env, + /*event_thread*/nullptr, + static_cast<ArtJvmtiEvent>(extension_event_index), + mode); } } // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_extension.h b/openjdkjvmti/ti_extension.h index d705ba7f59..18133e98e2 100644 --- a/openjdkjvmti/ti_extension.h +++ b/openjdkjvmti/ti_extension.h @@ -37,6 +37,8 @@ namespace openjdkjvmti { +class EventHandler; + class ExtensionUtil { public: static jvmtiError GetExtensionFunctions(jvmtiEnv* env, @@ -49,7 +51,8 @@ class ExtensionUtil { static jvmtiError SetExtensionEventCallback(jvmtiEnv* env, jint extension_event_index, - jvmtiExtensionEvent callback); + jvmtiExtensionEvent callback, + EventHandler* event_handler); }; } // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_monitor.cc b/openjdkjvmti/ti_monitor.cc index b31e9f239d..7db0566a2e 100644 --- a/openjdkjvmti/ti_monitor.cc +++ b/openjdkjvmti/ti_monitor.cc @@ -32,9 +32,9 @@ #include "ti_monitor.h" #include <atomic> -#include <chrono> // NOLINT [build/c++11] [5] -#include <condition_variable> // NOLINT [build/c++11] [5] -#include <mutex> // NOLINT [build/c++11] [5] +#include <chrono> +#include <condition_variable> +#include <mutex> #include "art_jvmti.h" #include "monitor.h" |