4DumpHprof

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

VMDebug_dumpHprofData

/*
 * 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 {

hprof::DumpHeap

// 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::ScopedSuspendAll(const char* cause, bool long_suspend) {
  Runtime::Current()->GetThreadList()->SuspendAll(cause, long_suspend);
}

hprof.Dump

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);
  }
}

ProcessBody

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();
}
StartNewRecord
void StartNewRecord(uint8_t tag, uint32_t time) {
  if (length_ > 0) {
    EndRecord();
  }
  AddU1(tag);
  AddU4(time);
  AddU4(0xdeaddead);  // Length, replaced on flush.
  started_ = true;
}
runtime->VisitRoots(this)–>Hprof::VisitRoot
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_;
runtime->GetHeap()->VisitObjectsPaused(dump_object)–>DumpHeapObject
obj->VisitReferences
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_;
DumpHeapInstanceObject
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));