| /* |
| * Copyright (C) 2016 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 "ti_heap.h" |
| |
| #include "art_jvmti.h" |
| #include "base/macros.h" |
| #include "base/mutex.h" |
| #include "class_linker.h" |
| #include "gc/heap.h" |
| #include "jni_env_ext.h" |
| #include "mirror/class.h" |
| #include "object_callbacks.h" |
| #include "object_tagging.h" |
| #include "obj_ptr-inl.h" |
| #include "runtime.h" |
| #include "scoped_thread_state_change-inl.h" |
| #include "thread-inl.h" |
| |
| namespace openjdkjvmti { |
| |
| struct IterateThroughHeapData { |
| IterateThroughHeapData(HeapUtil* _heap_util, |
| jint heap_filter, |
| art::ObjPtr<art::mirror::Class> klass, |
| const jvmtiHeapCallbacks* _callbacks, |
| const void* _user_data) |
| : heap_util(_heap_util), |
| filter_klass(klass), |
| callbacks(_callbacks), |
| user_data(_user_data), |
| filter_out_tagged((heap_filter & JVMTI_HEAP_FILTER_TAGGED) != 0), |
| filter_out_untagged((heap_filter & JVMTI_HEAP_FILTER_UNTAGGED) != 0), |
| filter_out_class_tagged((heap_filter & JVMTI_HEAP_FILTER_CLASS_TAGGED) != 0), |
| filter_out_class_untagged((heap_filter & JVMTI_HEAP_FILTER_CLASS_UNTAGGED) != 0), |
| any_filter(filter_out_tagged || |
| filter_out_untagged || |
| filter_out_class_tagged || |
| filter_out_class_untagged), |
| stop_reports(false) { |
| } |
| |
| bool ShouldReportByHeapFilter(jlong tag, jlong class_tag) { |
| if (!any_filter) { |
| return true; |
| } |
| |
| if ((tag == 0 && filter_out_untagged) || (tag != 0 && filter_out_tagged)) { |
| return false; |
| } |
| |
| if ((class_tag == 0 && filter_out_class_untagged) || |
| (class_tag != 0 && filter_out_class_tagged)) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| HeapUtil* heap_util; |
| art::ObjPtr<art::mirror::Class> filter_klass; |
| const jvmtiHeapCallbacks* callbacks; |
| const void* user_data; |
| const bool filter_out_tagged; |
| const bool filter_out_untagged; |
| const bool filter_out_class_tagged; |
| const bool filter_out_class_untagged; |
| const bool any_filter; |
| |
| bool stop_reports; |
| }; |
| |
| static void IterateThroughHeapObjectCallback(art::mirror::Object* obj, void* arg) |
| REQUIRES_SHARED(art::Locks::mutator_lock_) { |
| IterateThroughHeapData* ithd = reinterpret_cast<IterateThroughHeapData*>(arg); |
| // Early return, as we can't really stop visiting. |
| if (ithd->stop_reports) { |
| return; |
| } |
| |
| art::ScopedAssertNoThreadSuspension no_suspension("IterateThroughHeapCallback"); |
| |
| jlong tag = 0; |
| ithd->heap_util->GetTags()->GetTag(obj, &tag); |
| |
| jlong class_tag = 0; |
| art::ObjPtr<art::mirror::Class> klass = obj->GetClass(); |
| ithd->heap_util->GetTags()->GetTag(klass.Ptr(), &class_tag); |
| // For simplicity, even if we find a tag = 0, assume 0 = not tagged. |
| |
| if (!ithd->ShouldReportByHeapFilter(tag, class_tag)) { |
| return; |
| } |
| |
| // TODO: Handle array_primitive_value_callback. |
| |
| if (ithd->filter_klass != nullptr) { |
| if (ithd->filter_klass != klass) { |
| return; |
| } |
| } |
| |
| jlong size = obj->SizeOf(); |
| |
| jint length = -1; |
| if (obj->IsArrayInstance()) { |
| length = obj->AsArray()->GetLength(); |
| } |
| |
| jlong saved_tag = tag; |
| jint ret = ithd->callbacks->heap_iteration_callback(class_tag, |
| size, |
| &tag, |
| length, |
| const_cast<void*>(ithd->user_data)); |
| |
| if (tag != saved_tag) { |
| ithd->heap_util->GetTags()->Set(obj, tag); |
| } |
| |
| ithd->stop_reports = (ret & JVMTI_VISIT_ABORT) != 0; |
| |
| // TODO Implement array primitive and string primitive callback. |
| // TODO Implement primitive field callback. |
| } |
| |
| jvmtiError HeapUtil::IterateThroughHeap(jvmtiEnv* env ATTRIBUTE_UNUSED, |
| jint heap_filter, |
| jclass klass, |
| const jvmtiHeapCallbacks* callbacks, |
| const void* user_data) { |
| if (callbacks == nullptr) { |
| return ERR(NULL_POINTER); |
| } |
| |
| if (callbacks->array_primitive_value_callback != nullptr) { |
| // TODO: Implement. |
| return ERR(NOT_IMPLEMENTED); |
| } |
| |
| art::Thread* self = art::Thread::Current(); |
| art::ScopedObjectAccess soa(self); // Now we know we have the shared lock. |
| |
| IterateThroughHeapData ithd(this, |
| heap_filter, |
| soa.Decode<art::mirror::Class>(klass), |
| callbacks, |
| user_data); |
| |
| art::Runtime::Current()->GetHeap()->VisitObjects(IterateThroughHeapObjectCallback, &ithd); |
| |
| return ERR(NONE); |
| } |
| |
| jvmtiError HeapUtil::GetLoadedClasses(jvmtiEnv* env, |
| jint* class_count_ptr, |
| jclass** classes_ptr) { |
| if (class_count_ptr == nullptr || classes_ptr == nullptr) { |
| return ERR(NULL_POINTER); |
| } |
| |
| class ReportClassVisitor : public art::ClassVisitor { |
| public: |
| explicit ReportClassVisitor(art::Thread* self) : self_(self) {} |
| |
| bool operator()(art::ObjPtr<art::mirror::Class> klass) |
| OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { |
| classes_.push_back(self_->GetJniEnv()->AddLocalReference<jclass>(klass)); |
| return true; |
| } |
| |
| art::Thread* self_; |
| std::vector<jclass> classes_; |
| }; |
| |
| art::Thread* self = art::Thread::Current(); |
| ReportClassVisitor rcv(self); |
| { |
| art::ScopedObjectAccess soa(self); |
| art::Runtime::Current()->GetClassLinker()->VisitClasses(&rcv); |
| } |
| |
| size_t size = rcv.classes_.size(); |
| jclass* classes = nullptr; |
| jvmtiError alloc_ret = env->Allocate(static_cast<jlong>(size * sizeof(jclass)), |
| reinterpret_cast<unsigned char**>(&classes)); |
| if (alloc_ret != ERR(NONE)) { |
| return alloc_ret; |
| } |
| |
| for (size_t i = 0; i < size; ++i) { |
| classes[i] = rcv.classes_[i]; |
| } |
| *classes_ptr = classes; |
| *class_count_ptr = static_cast<jint>(size); |
| |
| return ERR(NONE); |
| } |
| |
| jvmtiError HeapUtil::ForceGarbageCollection(jvmtiEnv* env ATTRIBUTE_UNUSED) { |
| art::Runtime::Current()->GetHeap()->CollectGarbage(false); |
| |
| return ERR(NONE); |
| } |
| |
| } // namespace openjdkjvmti |