graph LR
subgraph Visit
runtime.VisitRoots
runtime.heap.VisitObjectsPaused
end
subgraph suspend
ScopedSuspendAll
end
subgraph Dump
DumpHeapClass
DumpHeapInstanceObject
DumpHeapArray
end
hprof::DumpHeap-->ScopedSuspendAll
hprof::DumpHeap-->runtime.VisitRoots-->Hprof::VisitRoot
hprof::DumpHeap-->runtime.heap.VisitObjectsPaused-->DumpHeapObject
DumpHeapObject-->obj.VisitReferences
DumpHeapObject-->DumpHeapClass
DumpHeapObject-->DumpHeapInstanceObject
DumpHeapObject-->DumpHeapArray
art/runtime/native/dalvik_system_VMDebug.cc
/*
* static void dumpHprofData(String fileName, FileDescriptor fd)
*
* Cause "hprof" data to be dumped. We can throw an IOException if an
* error occurs during file handling.
*/
static void VMDebug_dumpHprofData(JNIEnv* env, jclass, jstring javaFilename, jint javaFd) {
std::string filename;
if (javaFilename != nullptr) {
ScopedUtfChars chars(env, javaFilename);
if (env->ExceptionCheck()) {
return;
}
filename = chars.c_str();
} else {
filename = "[fd]";
}
int fd = javaFd;
hprof::DumpHeap(filename.c_str(), fd, false);
libnativehelper/header_only_include/nativehelper/scoped_utf_chars.h
ScopedUtfChars(JNIEnv* env, jstring s) : env_(env), string_(s) {
if (s == nullptr) {
utf_chars_ = nullptr;
jniThrowNullPointerException(env, nullptr);
} else {
utf_chars_ = env->GetStringUTFChars(s, nullptr);
}
}
art/runtime/hprof/hprof.cc
class Hprof : public SingleRootVisitor {
// If "direct_to_ddms" is true, the other arguments are ignored, and data is
// sent directly to DDMS.
// If "fd" is >= 0, the output will be written to that file descriptor.
// Otherwise, "filename" is used to create an output file.
void DumpHeap(const char* filename, int fd, bool direct_to_ddms) {
CHECK(filename != nullptr);
Thread* self = Thread::Current();
// Need to take a heap dump while GC isn't running. See the comment in Heap::VisitObjects().
// Also we need the critical section to avoid visiting the same object twice. See b/34967844
gc::ScopedGCCriticalSection gcs(self,
gc::kGcCauseHprof,
gc::kCollectorTypeHprof);
ScopedSuspendAll ssa(__FUNCTION__, true /* long suspend */);
Hprof hprof(filename, fd, direct_to_ddms);
hprof.Dump();
}
art/runtime/thread_list.cc
ScopedSuspendAll::ScopedSuspendAll(const char* cause, bool long_suspend) {
Runtime::Current()->GetThreadList()->SuspendAll(cause, long_suspend);
}
void Dump()
REQUIRES(Locks::mutator_lock_)
REQUIRES(!Locks::heap_bitmap_lock_, !Locks::alloc_tracker_lock_) {
{
// First pass to measure the size of the dump.
size_t overall_size;
size_t max_length;
{
EndianOutput count_output;
output_ = &count_output;
ProcessHeap(false);
overall_size = count_output.SumLength();
max_length = count_output.MaxLength();
output_ = nullptr;
}
void ProcessHeap(bool header_first)
REQUIRES(Locks::mutator_lock_) {
......
if (header_first) {
ProcessHeader(true);
ProcessBody();
} else {
ProcessBody();
ProcessHeader(false);
}
}
void ProcessBody() REQUIRES(Locks::mutator_lock_) {
Runtime* const runtime = Runtime::Current();
// Walk the roots and the heap.
output_->StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, kHprofTime);
simple_roots_.clear();
runtime->VisitRoots(this);
runtime->VisitImageRoots(this);
//method callback params
auto dump_object = [this](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(obj != nullptr);
DumpHeapObject(obj);
};
runtime->GetHeap()->VisitObjectsPaused(dump_object);
output_->StartNewRecord(HPROF_TAG_HEAP_DUMP_END, kHprofTime);
output_->EndRecord();
}
void StartNewRecord(uint8_t tag, uint32_t time) {
if (length_ > 0) {
EndRecord();
}
AddU1(tag);
AddU4(time);
AddU4(0xdeaddead); // Length, replaced on flush.
started_ = true;
}
void Hprof::VisitRoot(mirror::Object* obj, const RootInfo& info) {
static const HprofHeapTag xlate[] = {
HPROF_ROOT_UNKNOWN,
HPROF_ROOT_JNI_GLOBAL,
HPROF_ROOT_JNI_LOCAL,
HPROF_ROOT_JAVA_FRAME,
HPROF_ROOT_NATIVE_STACK,
HPROF_ROOT_STICKY_CLASS,
HPROF_ROOT_THREAD_BLOCK,
HPROF_ROOT_MONITOR_USED,
HPROF_ROOT_THREAD_OBJECT,
HPROF_ROOT_INTERNED_STRING,
HPROF_ROOT_FINALIZING,
HPROF_ROOT_DEBUGGER,
HPROF_ROOT_REFERENCE_CLEANUP,
HPROF_ROOT_VM_INTERNAL,
HPROF_ROOT_JNI_MONITOR,
};
CHECK_LT(info.GetType(), sizeof(xlate) / sizeof(HprofHeapTag));
if (obj == nullptr) {
return;
}
MarkRootObject(obj, 0, xlate[info.GetType()], info.GetThreadId());
}
void Hprof::MarkRootObject(const mirror::Object* obj, jobject jni_obj, HprofHeapTag heap_tag,
uint32_t thread_serial) {
switch (heap_tag) {
// ID: object ID
case HPROF_ROOT_UNKNOWN:
case HPROF_ROOT_STICKY_CLASS:
case HPROF_ROOT_MONITOR_USED:
case HPROF_ROOT_INTERNED_STRING:
case HPROF_ROOT_DEBUGGER:
case HPROF_ROOT_VM_INTERNAL: {
uint64_t key = (static_cast<uint64_t>(heap_tag) << 32) | PointerToLowMemUInt32(obj);
if (simple_roots_.insert(key).second) {
__ AddU1(heap_tag);
__ AddObjectId(obj);
}
break;
}
......
// ID: thread object ID
// U4: thread serial number
// U4: stack trace serial number
case HPROF_ROOT_THREAD_OBJECT:
__ AddU1(heap_tag);
__ AddObjectId(obj);
__ AddU4(thread_serial);
__ AddU4((uint32_t)-1); // xxx
break;
case HPROF_CLASS_DUMP:
case HPROF_INSTANCE_DUMP:
case HPROF_OBJECT_ARRAY_DUMP:
case HPROF_PRIMITIVE_ARRAY_DUMP:
case HPROF_HEAP_DUMP_INFO:
case HPROF_PRIMITIVE_ARRAY_NODATA_DUMP:
// Ignored.
break;
case HPROF_ROOT_FINALIZING:
case HPROF_ROOT_REFERENCE_CLEANUP:
case HPROF_UNREACHABLE:
LOG(FATAL) << "obsolete tag " << static_cast<int>(heap_tag);
break;
}
++objects_in_segment_;
void Hprof::DumpHeapObject(mirror::Object* obj) {
// Ignore classes that are retired.
if (obj->IsClass() && obj->AsClass()->IsRetired()) {
return;
}
++total_objects_;
RootCollector visitor;
// Collect all native roots.
if (!obj->IsClass()) {
obj->VisitReferences(visitor, VoidFunctor());
}
......
mirror::Class* c = obj->GetClass();
if (c == nullptr) {
// This object will bother HprofReader, because it has a null
// class, so just don't dump it. It could be
// gDvm.unlinkedJavaLangClass or it could be an object just
// allocated which hasn't been initialized yet.
} else {
if (obj->IsClass()) {
DumpHeapClass(obj->AsClass()); //HPROF_CLASS_DUMP
} else if (c->IsArrayClass()) {
DumpHeapArray(obj->AsArray(), c); //HPROF_OBJECT_ARRAY_DUMP or HPROF_PRIMITIVE_ARRAY_DUMP
} else {
DumpHeapInstanceObject(obj, c, visitor.GetRoots()); //HPROF_INSTANCE_DUMP
}
}
++objects_in_segment_;
void Hprof::DumpHeapInstanceObject(mirror::Object* obj,
mirror::Class* klass,
const std::set<mirror::Object*>& fake_roots) {
// obj is an instance object.
__ AddU1(HPROF_INSTANCE_DUMP);
__ AddObjectId(obj);
__ AddStackTraceSerialNumber(LookupStackTraceSerialNumber(obj));
__ AddClassId(LookupClassId(klass));
// Reserve some space for the length of the instance data, which we won't
// know until we're done writing it.
size_t size_patch_offset = output_->Length();
__ AddU4(0x77777777);
// Write the instance data; fields for this class, followed by super class fields, and so on.
do {
const size_t instance_fields = klass->NumInstanceFields();
for (size_t i = 0; i < instance_fields; ++i) {
ArtField* f = klass->GetInstanceField(i);
size_t size;
HprofBasicType t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size);
switch (t) {
case hprof_basic_byte:
__ AddU1(f->GetByte(obj));
break;
case hprof_basic_boolean:
__ AddU1(f->GetBoolean(obj));
break;
case hprof_basic_char:
__ AddU2(f->GetChar(obj));
break;
......
case hprof_basic_double:
case hprof_basic_long:
__ AddU8(f->Get64(obj));
break;
}
}
klass = klass->GetSuperClass();
} while (klass != nullptr);
// Patch the instance field length.
__ UpdateU4(size_patch_offset, output_->Length() - (size_patch_offset + 4));