blob: 6b207430ff0c3cd086c7e73e75b2b189db4c55a7 [file] [log] [blame]
/*
* 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