Clean up the hprof implementation a bit.

This includes the art equivalent of 4769b5106ff47fef1df9491608f0a2ba3354b9d3,
which I think is quite a bit clearer here (see the start of Hprof::Dump).

Change-Id: I4f31a1f22e0d122663f808ebeb3d7ab4968e9b47
diff --git a/src/hprof/hprof.cc b/src/hprof/hprof.cc
index d59d179..3e976ad 100644
--- a/src/hprof/hprof.cc
+++ b/src/hprof/hprof.cc
@@ -45,6 +45,7 @@
 #include "logging.h"
 #include "object.h"
 #include "object_utils.h"
+#include "os.h"
 #include "safe_map.h"
 #include "scoped_heap_lock.h"
 #include "stringprintf.h"
@@ -138,7 +139,7 @@
   HPROF_ROOT_VM_INTERNAL = 0x8d,
   HPROF_ROOT_JNI_MONITOR = 0x8e,
   HPROF_UNREACHABLE = 0x90,  // Obsolete.
-  HPROF_PRIMITIVE_ARRAY_NODATA_DUMP = 0xc3,
+  HPROF_PRIMITIVE_ARRAY_NODATA_DUMP = 0xc3,  // Obsolete.
 };
 
 enum HprofHeapId {
@@ -175,7 +176,32 @@
 // U1* BODY: as many bytes as specified in the above uint32_t field
 class HprofRecord {
  public:
-  int Flush(FILE* fp) {
+  HprofRecord() {
+    dirty_ = false;
+    alloc_length_ = 128;
+    body_ = reinterpret_cast<unsigned char*>(malloc(alloc_length_));
+    fp_ = NULL;
+  }
+
+  ~HprofRecord() {
+    free(body_);
+  }
+
+  int StartNewRecord(FILE* fp, uint8_t tag, uint32_t time) {
+    int rc = Flush();
+    if (rc != 0) {
+      return rc;
+    }
+
+    fp_ = fp;
+    tag_ = tag;
+    time_ = time;
+    length_ = 0;
+    dirty_ = true;
+    return 0;
+  }
+
+  int Flush() {
     if (dirty_) {
       unsigned char headBuf[sizeof(uint8_t) + 2 * sizeof(uint32_t)];
 
@@ -183,11 +209,11 @@
       U4_TO_BUF_BE(headBuf, 1, time_);
       U4_TO_BUF_BE(headBuf, 5, length_);
 
-      int nb = fwrite(headBuf, 1, sizeof(headBuf), fp);
+      int nb = fwrite(headBuf, 1, sizeof(headBuf), fp_);
       if (nb != sizeof(headBuf)) {
         return UNIQUE_ERROR;
       }
-      nb = fwrite(body_, 1, length_, fp);
+      nb = fwrite(body_, 1, length_, fp_);
       if (nb != (int)length_) {
         return UNIQUE_ERROR;
       }
@@ -224,7 +250,7 @@
     return AddU4((uint32_t) value);
   }
 
-  int AddU1List(const uint8_t *values, size_t numValues) {
+  int AddU1List(const uint8_t* values, size_t numValues) {
     int err = GuaranteeRecordAppend(numValues);
     if (err != 0) {
       return err;
@@ -235,14 +261,14 @@
     return 0;
   }
 
-  int AddU2List(const uint16_t *values, size_t numValues) {
+  int AddU2List(const uint16_t* values, size_t numValues) {
     int err = GuaranteeRecordAppend(numValues * 2);
     if (err != 0) {
       return err;
     }
 
     unsigned char* insert = body_ + length_;
-    for (size_t i = 0; i < numValues; i++) {
+    for (size_t i = 0; i < numValues; ++i) {
       U2_TO_BUF_BE(insert, 0, *values++);
       insert += sizeof(*values);
     }
@@ -250,14 +276,14 @@
     return 0;
   }
 
-  int AddU4List(const uint32_t *values, size_t numValues) {
+  int AddU4List(const uint32_t* values, size_t numValues) {
     int err = GuaranteeRecordAppend(numValues * 4);
     if (err != 0) {
       return err;
     }
 
     unsigned char* insert = body_ + length_;
-    for (size_t i = 0; i < numValues; i++) {
+    for (size_t i = 0; i < numValues; ++i) {
       U4_TO_BUF_BE(insert, 0, *values++);
       insert += sizeof(*values);
     }
@@ -265,14 +291,18 @@
     return 0;
   }
 
-  int AddU8List(const uint64_t *values, size_t numValues) {
+  void UpdateU4(size_t offset, uint32_t new_value) {
+    U4_TO_BUF_BE(body_, offset, new_value);
+  }
+
+  int AddU8List(const uint64_t* values, size_t numValues) {
     int err = GuaranteeRecordAppend(numValues * 8);
     if (err != 0) {
       return err;
     }
 
     unsigned char* insert = body_ + length_;
-    for (size_t i = 0; i < numValues; i++) {
+    for (size_t i = 0; i < numValues; ++i) {
       U8_TO_BUF_BE(insert, 0, *values++);
       insert += sizeof(*values);
     }
@@ -280,21 +310,18 @@
     return 0;
   }
 
-  int AddIdList(const HprofObjectId *values, size_t numValues) {
+  int AddIdList(const HprofObjectId* values, size_t numValues) {
     return AddU4List((const uint32_t*) values, numValues);
   }
 
   int AddUtf8String(const char* str) {
     // The terminating NUL character is NOT written.
-    return AddU1List((const uint8_t *)str, strlen(str));
+    return AddU1List((const uint8_t*)str, strlen(str));
   }
 
-  unsigned char* body_;
-  uint32_t time_;
-  uint32_t length_;
-  size_t alloc_length_;
-  uint8_t tag_;
-  bool dirty_;
+  size_t Size() const {
+    return length_;
+  }
 
  private:
   int GuaranteeRecordAppend(size_t nmore) {
@@ -317,99 +344,261 @@
     CHECK_LE(length_ + nmore, alloc_length_);
     return 0;
   }
+
+  size_t alloc_length_;
+  unsigned char* body_;
+
+  FILE* fp_;
+  uint8_t tag_;
+  uint32_t time_;
+  size_t length_;
+  bool dirty_;
+
+  DISALLOW_COPY_AND_ASSIGN(HprofRecord);
 };
 
 class Hprof {
  public:
-  Hprof(const char* output_filename, int fd, bool write_header, bool direct_to_ddms);
-  ~Hprof();
+  Hprof(const char* output_filename, int fd, bool direct_to_ddms)
+      : filename_(output_filename),
+        fd_(fd),
+        direct_to_ddms_(direct_to_ddms),
+        start_ns_(NanoTime()),
+        current_record_(),
+        gc_thread_serial_number_(0),
+        gc_scan_state_(0),
+        current_heap_(HPROF_HEAP_DEFAULT),
+        objects_in_segment_(0),
+        header_fp_(NULL),
+        header_data_ptr_(NULL),
+        header_data_size_(0),
+        body_fp_(NULL),
+        body_data_ptr_(NULL),
+        body_data_size_(0),
+        next_string_id_(0x400000) {
+    LOG(INFO) << "hprof: heap dump \"" << filename_ << "\" starting...";
 
-  void VisitRoot(const Object* obj);
-  int DumpHeapObject(const Object *obj);
-  void Finish();
+    header_fp_ = open_memstream(&header_data_ptr_, &header_data_size_);
+    if (header_fp_ == NULL) {
+      PLOG(FATAL) << "header open_memstream failed";
+    }
 
- private:
-  int DumpClasses();
-  int DumpStrings();
-  int StartNewRecord(uint8_t tag, uint32_t time);
-  int FlushCurrentRecord();
-  int MarkRootObject(const Object *obj, jobject jniObj);
-  HprofClassObjectId LookupClassId(const Class* c);
-  HprofStringId LookupStringId(String* string);
-  HprofStringId LookupStringId(const char* string);
-  HprofStringId LookupStringId(const std::string& string);
-  HprofStringId LookupClassNameId(const Class* c);
-
-  // current_record_ *must* be first so that we can cast from a context to a record.
-  HprofRecord current_record_;
-
-  uint32_t gc_thread_serial_number_;
-  uint8_t gc_scan_state_;
-  HprofHeapId current_heap_; // which heap we're currently emitting
-  size_t objects_in_segment_;
-
-  // If direct_to_ddms_ is set, "file_name_" and "fd" will be ignored.
-  // Otherwise, "file_name_" must be valid, though if "fd" >= 0 it will
-  // only be used for debug messages.
-  bool direct_to_ddms_;
-  std::string file_name_;
-  char* file_data_ptr_;   // for open_memstream
-  size_t file_data_size_; // for open_memstream
-  FILE *mem_fp_;
-  int fd_;
-
-  ClassSet classes_;
-  size_t next_string_id_;
-  StringMap strings_;
-
-  DISALLOW_COPY_AND_ASSIGN(Hprof);
-};
-
-Hprof::Hprof(const char* output_filename, int fd, bool write_header, bool direct_to_ddms)
-    : current_record_(),
-      gc_thread_serial_number_(0),
-      gc_scan_state_(0),
-      current_heap_(HPROF_HEAP_DEFAULT),
-      objects_in_segment_(0),
-      direct_to_ddms_(0),
-      file_name_(output_filename),
-      file_data_ptr_(NULL),
-      file_data_size_(0),
-      mem_fp_(NULL),
-      fd_(0),
-      next_string_id_(0x400000) {
-
-  LOG(INFO) << "hprof: heap dump starting (\"" << output_filename << "\", fd=" << fd
-            << ", write_header=" << write_header << ", direct_to_ddms=" << direct_to_ddms << ")";
-
-  // Have to do this here, because it must happen after we
-  // memset the struct (want to treat file_data_ptr_/file_data_size_
-  // as read-only while the file is open).
-  FILE *fp = open_memstream(&file_data_ptr_, &file_data_size_);
-  if (fp == NULL) {
-    // not expected
-    PLOG(FATAL) << "open_memstream failed";
+    body_fp_ = open_memstream(&body_data_ptr_, &body_data_size_);
+    if (body_fp_ == NULL) {
+      PLOG(FATAL) << "body open_memstream failed";
+    }
   }
 
-  direct_to_ddms_ = direct_to_ddms;
-  mem_fp_ = fp;
-  fd_ = fd;
+  ~Hprof() {
+    if (header_fp_ != NULL) {
+      fclose(header_fp_);
+    }
+    if (body_fp_ != NULL) {
+      fclose(body_fp_);
+    }
+    free(header_data_ptr_);
+    free(body_data_ptr_);
+  }
 
-  current_record_.alloc_length_ = 128;
-  current_record_.body_ = (unsigned char*)malloc(current_record_.alloc_length_);
-  // TODO check for/return an error
+  void Dump() {
+    // Walk the roots and the heap.
+    current_record_.StartNewRecord(body_fp_, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
+    Runtime::Current()->VisitRoots(RootVisitor, this);
+    Runtime::Current()->GetHeap()->GetLiveBits()->Walk(HeapBitmapCallback, this);
+    current_record_.StartNewRecord(body_fp_, HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
+    current_record_.Flush();
+    fflush(body_fp_);
 
-  if (write_header) {
+    // Write the header.
+    WriteFixedHeader();
+    // Write the string and class tables, and any stack traces, to the header.
+    // (jhat requires that these appear before any of the data in the body that refers to them.)
+    WriteStringTable();
+    WriteClassTable();
+    WriteStackTraces();
+    current_record_.Flush();
+    fflush(header_fp_);
+
+    bool okay = true;
+    if (direct_to_ddms_) {
+      // Send the data off to DDMS.
+      iovec iov[2];
+      iov[0].iov_base = header_data_ptr_;
+      iov[0].iov_len = header_data_size_;
+      iov[1].iov_base = body_data_ptr_;
+      iov[1].iov_len = body_data_size_;
+      Dbg::DdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
+    } else {
+      // Where exactly are we writing to?
+      int out_fd;
+      if (fd_ >= 0) {
+        out_fd = dup(fd_);
+        if (out_fd < 0) {
+          Thread::Current()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", "Couldn't dump heap; dup(%d) failed: %s", fd_, strerror(errno));
+          return;
+        }
+      } else {
+        out_fd = open(filename_.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0644);
+        if (out_fd < 0) {
+          Thread::Current()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", "Couldn't dump heap; open(\"%s\") failed: %s", filename_.c_str(), strerror(errno));
+          return;
+        }
+      }
+
+      UniquePtr<File> file(OS::FileFromFd(filename_.c_str(), out_fd));
+      okay = file->WriteFully(header_data_ptr_, header_data_size_) && file->WriteFully(body_data_ptr_, body_data_size_);
+      if (!okay) {
+        std::string msg(StringPrintf("Couldn't dump heap; writing \"%s\" failed: %s", filename_.c_str(), strerror(errno)));
+        Thread::Current()->ThrowNewException("Ljava/lang/RuntimeException;", msg.c_str());
+        LOG(ERROR) << msg;
+      }
+      close(out_fd);
+    }
+
+    // Throw out a log message for the benefit of "runhat".
+    if (okay) {
+      uint64_t duration = NanoTime() - start_ns_;
+      LOG(INFO) << "hprof: heap dump completed (" << PrettySize(header_data_size_ + body_data_size_ + 1023) << ") in " << PrettyDuration(duration);
+    }
+  }
+
+ private:
+  static void RootVisitor(const Object* obj, void* arg) {
+    CHECK(arg != NULL);
+    Hprof* hprof = reinterpret_cast<Hprof*>(arg);
+    hprof->VisitRoot(obj);
+  }
+
+  static void HeapBitmapCallback(Object* obj, void* arg) {
+    CHECK(obj != NULL);
+    CHECK(arg != NULL);
+    Hprof* hprof = reinterpret_cast<Hprof*>(arg);
+    hprof->DumpHeapObject(obj);
+  }
+
+  void VisitRoot(const Object* obj);
+
+  int DumpHeapObject(const Object* obj);
+
+  void Finish() {
+  }
+
+  int WriteClassTable() {
+    HprofRecord* rec = &current_record_;
+    uint32_t nextSerialNumber = 1;
+
+    for (ClassSetIterator it = classes_.begin(); it != classes_.end(); ++it) {
+      const Class* c = *it;
+      CHECK(c != NULL);
+
+      int err = current_record_.StartNewRecord(header_fp_, HPROF_TAG_LOAD_CLASS, HPROF_TIME);
+      if (err != 0) {
+        return err;
+      }
+
+      // LOAD CLASS format:
+      // U4: class serial number (always > 0)
+      // ID: class object ID. We use the address of the class object structure as its ID.
+      // U4: stack trace serial number
+      // ID: class name string ID
+      rec->AddU4(nextSerialNumber++);
+      rec->AddId((HprofClassObjectId) c);
+      rec->AddU4(HPROF_NULL_STACK_TRACE);
+      rec->AddId(LookupClassNameId(c));
+    }
+
+    return 0;
+  }
+
+  int WriteStringTable() {
+    HprofRecord* rec = &current_record_;
+
+    for (StringMapIterator it = strings_.begin(); it != strings_.end(); ++it) {
+      std::string string((*it).first);
+      size_t id = (*it).second;
+
+      int err = current_record_.StartNewRecord(header_fp_, HPROF_TAG_STRING, HPROF_TIME);
+      if (err != 0) {
+        return err;
+      }
+
+      // STRING format:
+      // ID:  ID for this string
+      // U1*: UTF8 characters for string (NOT NULL terminated)
+      //      (the record format encodes the length)
+      err = rec->AddU4(id);
+      if (err != 0) {
+        return err;
+      }
+      err = rec->AddUtf8String(string.c_str());
+      if (err != 0) {
+        return err;
+      }
+    }
+
+    return 0;
+  }
+
+  void StartNewHeapDumpSegment() {
+    // This flushes the old segment and starts a new one.
+    current_record_.StartNewRecord(body_fp_, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
+    objects_in_segment_ = 0;
+
+    // Starting a new HEAP_DUMP resets the heap to default.
+    current_heap_ = HPROF_HEAP_DEFAULT;
+  }
+
+  int MarkRootObject(const Object* obj, jobject jniObj);
+
+  HprofClassObjectId LookupClassId(const Class* c) {
+    if (c == NULL) {
+      // c is the superclass of java.lang.Object or a primitive
+      return (HprofClassObjectId)0;
+    }
+
+    std::pair<ClassSetIterator, bool> result = classes_.insert(c);
+    const Class* present = *result.first;
+
+    // Make sure that we've assigned a string ID for this class' name
+    LookupClassNameId(c);
+
+    CHECK_EQ(present, c);
+    return (HprofStringId) present;
+  }
+
+  HprofStringId LookupStringId(String* string) {
+    return LookupStringId(string->ToModifiedUtf8());
+  }
+
+  HprofStringId LookupStringId(const char* string) {
+    return LookupStringId(std::string(string));
+  }
+
+  HprofStringId LookupStringId(const std::string& string) {
+    StringMapIterator it = strings_.find(string);
+    if (it != strings_.end()) {
+      return it->second;
+    }
+    HprofStringId id = next_string_id_++;
+    strings_.Put(string, id);
+    return id;
+  }
+
+  HprofStringId LookupClassNameId(const Class* c) {
+    return LookupStringId(PrettyDescriptor(c));
+  }
+
+  void WriteFixedHeader() {
     char magic[] = "JAVA PROFILE 1.0.3";
     unsigned char buf[4];
 
     // Write the file header.
     // U1: NUL-terminated magic string.
-    fwrite(magic, 1, sizeof(magic), fp);
+    fwrite(magic, 1, sizeof(magic), header_fp_);
 
     // U4: size of identifiers.  We're using addresses as IDs, so make sure a pointer fits.
-    U4_TO_BUF_BE(buf, 0, sizeof(void *));
-    fwrite(buf, 1, sizeof(uint32_t), fp);
+    U4_TO_BUF_BE(buf, 0, sizeof(void*));
+    fwrite(buf, 1, sizeof(uint32_t), header_fp_);
 
     // The current time, in milliseconds since 0:00 GMT, 1/1/70.
     timeval now;
@@ -422,39 +611,51 @@
 
     // U4: high word of the 64-bit time.
     U4_TO_BUF_BE(buf, 0, (uint32_t)(nowMs >> 32));
-    fwrite(buf, 1, sizeof(uint32_t), fp);
+    fwrite(buf, 1, sizeof(uint32_t), header_fp_);
 
     // U4: low word of the 64-bit time.
     U4_TO_BUF_BE(buf, 0, (uint32_t)(nowMs & 0xffffffffULL));
-    fwrite(buf, 1, sizeof(uint32_t), fp); //xxx fix the time
-  }
-}
-
-int Hprof::StartNewRecord(uint8_t tag, uint32_t time) {
-  HprofRecord *rec = &current_record_;
-
-  int err = rec->Flush(mem_fp_);
-  if (err != 0) {
-    return err;
-  } else if (rec->dirty_) {
-    return UNIQUE_ERROR;
+    fwrite(buf, 1, sizeof(uint32_t), header_fp_); //xxx fix the time
   }
 
-  rec->dirty_ = true;
-  rec->tag_ = tag;
-  rec->time_ = time;
-  rec->length_ = 0;
-  return 0;
-}
+  void WriteStackTraces() {
+    // Write a dummy stack trace record so the analysis tools don't freak out.
+    current_record_.StartNewRecord(header_fp_, HPROF_TAG_STACK_TRACE, HPROF_TIME);
+    current_record_.AddU4(HPROF_NULL_STACK_TRACE);
+    current_record_.AddU4(HPROF_NULL_THREAD);
+    current_record_.AddU4(0);    // no frames
+  }
 
-int Hprof::FlushCurrentRecord() {
-  return current_record_.Flush(mem_fp_);
-}
+  // If direct_to_ddms_ is set, "filename_" and "fd" will be ignored.
+  // Otherwise, "filename_" must be valid, though if "fd" >= 0 it will
+  // only be used for debug messages.
+  std::string filename_;
+  int fd_;
+  bool direct_to_ddms_;
 
-// Set DUMP_PRIM_DATA to 1 if you want to include the contents
-// of primitive arrays (byte arrays, character arrays, etc.)
-// in heap dumps.  This can be a large amount of data.
-#define DUMP_PRIM_DATA 1
+  uint64_t start_ns_;
+
+  HprofRecord current_record_;
+
+  uint32_t gc_thread_serial_number_;
+  uint8_t gc_scan_state_;
+  HprofHeapId current_heap_; // Which heap we're currently dumping.
+  size_t objects_in_segment_;
+
+  FILE* header_fp_;
+  char* header_data_ptr_;
+  size_t header_data_size_;
+
+  FILE* body_fp_;
+  char* body_data_ptr_;
+  size_t body_data_size_;
+
+  ClassSet classes_;
+  size_t next_string_id_;
+  StringMap strings_;
+
+  DISALLOW_COPY_AND_ASSIGN(Hprof);
+};
 
 #define OBJECTS_PER_SEGMENT     ((size_t)128)
 #define BYTES_PER_SEGMENT       ((size_t)4096)
@@ -491,7 +692,7 @@
   return ret;
 }
 
-static HprofBasicType PrimitiveToBasicTypeAndSize(Primitive::Type prim, size_t *sizeOut) {
+static HprofBasicType PrimitiveToBasicTypeAndSize(Primitive::Type prim, size_t* sizeOut) {
   HprofBasicType ret;
   size_t size;
 
@@ -518,7 +719,7 @@
 // something when ctx->gc_scan_state_ is non-zero, which is usually
 // only true when marking the root set or unreachable
 // objects.  Used to add rootset references to obj.
-int Hprof::MarkRootObject(const Object *obj, jobject jniObj) {
+int Hprof::MarkRootObject(const Object* obj, jobject jniObj) {
   HprofRecord* rec = &current_record_;
   HprofHeapTag heapTag = (HprofHeapTag)gc_scan_state_;
 
@@ -526,10 +727,8 @@
     return 0;
   }
 
-  if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->length_ >= BYTES_PER_SEGMENT) {
-    // This flushes the old segment and starts a new one.
-    StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
-    objects_in_segment_ = 0;
+  if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->Size() >= BYTES_PER_SEGMENT) {
+    StartNewHeapDumpSegment();
   }
 
   switch (heapTag) {
@@ -599,25 +798,20 @@
     break;
   }
 
-  objects_in_segment_++;
+  ++objects_in_segment_;
   return 0;
 }
 
-static int StackTraceSerialNumber(const void* /*obj*/) {
+static int StackTraceSerialNumber(const Object* /*obj*/) {
   return HPROF_NULL_STACK_TRACE;
 }
 
 int Hprof::DumpHeapObject(const Object* obj) {
-  HprofRecord *rec = &current_record_;
+  HprofRecord* rec = &current_record_;
   HprofHeapId desiredHeap = false ? HPROF_HEAP_ZYGOTE : HPROF_HEAP_APP; // TODO: zygote objects?
 
-  if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->length_ >= BYTES_PER_SEGMENT) {
-    // This flushes the old segment and starts a new one.
-    StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
-    objects_in_segment_ = 0;
-
-    // Starting a new HEAP_DUMP resets the heap to default.
-    current_heap_ = HPROF_HEAP_DEFAULT;
+  if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->Size() >= BYTES_PER_SEGMENT) {
+    StartNewHeapDumpSegment();
   }
 
   if (desiredHeap != current_heap_) {
@@ -664,7 +858,7 @@
         rec->AddU4(StackTraceSerialNumber(obj));
         rec->AddU4(byteLength);
         rec->AddU1(hprof_basic_byte);
-        for (int i = 0; i < byteLength; i++) {
+        for (int i = 0; i < byteLength; ++i) {
           rec->AddU1(0);
         }
       }
@@ -747,35 +941,29 @@
         rec->AddId(LookupClassId(c));
 
         // Dump the elements, which are always objects or NULL.
-        rec->AddIdList((const HprofObjectId *)aobj->GetRawData(sizeof(Object*)), length);
+        rec->AddIdList((const HprofObjectId*)aobj->GetRawData(sizeof(Object*)), length);
       } else {
         size_t size;
         HprofBasicType t = PrimitiveToBasicTypeAndSize(c->GetComponentType()->GetPrimitiveType(), &size);
 
         // obj is a primitive array.
-#if DUMP_PRIM_DATA
         rec->AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
-#else
-        rec->AddU1(HPROF_PRIMITIVE_ARRAY_NODATA_DUMP);
-#endif
 
         rec->AddId((HprofObjectId)obj);
         rec->AddU4(StackTraceSerialNumber(obj));
         rec->AddU4(length);
         rec->AddU1(t);
 
-#if DUMP_PRIM_DATA
         // Dump the raw, packed element values.
         if (size == 1) {
-          rec->AddU1List((const uint8_t *)aobj->GetRawData(sizeof(uint8_t)), length);
+          rec->AddU1List((const uint8_t*)aobj->GetRawData(sizeof(uint8_t)), length);
         } else if (size == 2) {
-          rec->AddU2List((const uint16_t *)(void *)aobj->GetRawData(sizeof(uint16_t)), length);
+          rec->AddU2List((const uint16_t*)(void*)aobj->GetRawData(sizeof(uint16_t)), length);
         } else if (size == 4) {
-          rec->AddU4List((const uint32_t *)(void *)aobj->GetRawData(sizeof(uint32_t)), length);
+          rec->AddU4List((const uint32_t*)(void*)aobj->GetRawData(sizeof(uint32_t)), length);
         } else if (size == 8) {
-          rec->AddU8List((const uint64_t *)aobj->GetRawData(sizeof(uint64_t)), length);
+          rec->AddU8List((const uint64_t*)aobj->GetRawData(sizeof(uint64_t)), length);
         }
-#endif
       }
     } else {
       // obj is an instance object.
@@ -786,7 +974,7 @@
 
       // Reserve some space for the length of the instance data, which we won't
       // know until we're done writing it.
-      size_t sizePatchOffset = rec->length_;
+      size_t size_patch_offset = rec->Size();
       rec->AddU4(0x77777777);
 
       // Write the instance data;  fields for this class, followed by super class fields,
@@ -795,7 +983,7 @@
       FieldHelper fh;
       while (!sclass->IsObjectClass()) {
         int ifieldCount = sclass->NumInstanceFields();
-        for (int i = 0; i < ifieldCount; i++) {
+        for (int i = 0; i < ifieldCount; ++i) {
           Field* f = sclass->GetInstanceField(i);
           fh.ChangeField(f);
           size_t size;
@@ -817,116 +1005,17 @@
       }
 
       // Patch the instance field length.
-      size_t savedLen = rec->length_;
-      rec->length_ = sizePatchOffset;
-      rec->AddU4(savedLen - (sizePatchOffset + 4));
-      rec->length_ = savedLen;
+      rec->UpdateU4(size_patch_offset, rec->Size() - (size_patch_offset + 4));
     }
   }
 
-  objects_in_segment_++;
+  ++objects_in_segment_;
   return 0;
 }
 
-#define kHeadSuffix "-hptemp"
-
-// TODO: use File::WriteFully
-int sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg) {
-  while (count != 0) {
-    ssize_t actual = TEMP_FAILURE_RETRY(write(fd, buf, count));
-    if (actual < 0) {
-      int err = errno;
-      PLOG(ERROR) << StringPrintf("%s: write failed", logMsg);
-      return err;
-    } else if (actual != (ssize_t) count) {
-      LOG(DEBUG) << StringPrintf("%s: partial write (will retry): (%d of %zd)",
-          logMsg, (int) actual, count);
-      buf = (const void*) (((const uint8_t*) buf) + actual);
-    }
-    count -= actual;
-  }
-  return 0;
-}
-
-void Hprof::Finish() {
-  // flush the "tail" portion of the output
-  StartNewRecord(HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
-  FlushCurrentRecord();
-
-  // create a new Hprof for the start of the file (as opposed to this, which is the tail)
-  Hprof headCtx(file_name_.c_str(), fd_, true, direct_to_ddms_);
-  headCtx.classes_ = classes_;
-  headCtx.strings_ = strings_;
-
-  headCtx.DumpStrings();
-  headCtx.DumpClasses();
-
-  // write a dummy stack trace record so the analysis tools don't freak out
-  headCtx.StartNewRecord(HPROF_TAG_STACK_TRACE, HPROF_TIME);
-  headCtx.current_record_.AddU4(HPROF_NULL_STACK_TRACE);
-  headCtx.current_record_.AddU4(HPROF_NULL_THREAD);
-  headCtx.current_record_.AddU4(0);    // no frames
-
-  headCtx.FlushCurrentRecord();
-
-  // flush to ensure memstream pointer and size are updated
-  fflush(headCtx.mem_fp_);
-  fflush(mem_fp_);
-
-  if (direct_to_ddms_) {
-    // send the data off to DDMS
-    iovec iov[2];
-    iov[0].iov_base = headCtx.file_data_ptr_;
-    iov[0].iov_len = headCtx.file_data_size_;
-    iov[1].iov_base = file_data_ptr_;
-    iov[1].iov_len = file_data_size_;
-    Dbg::DdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
-  } else {
-    // open the output file, and copy the head and tail to it.
-    CHECK_EQ(headCtx.fd_, fd_);
-
-    int outFd;
-    if (headCtx.fd_ >= 0) {
-      outFd = dup(headCtx.fd_);
-      if (outFd < 0) {
-        Thread::Current()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", "Couldn't dump heap; dup(%d) failed: %s", headCtx.fd_, strerror(errno));
-        return;
-      }
-    } else {
-      outFd = open(file_name_.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0644);
-      if (outFd < 0) {
-        Thread::Current()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", "Couldn't dump heap; open(\"%s\") failed: %s", headCtx.file_name_.c_str(), strerror(errno));
-        return;
-      }
-    }
-
-    // TODO: just use writev(2)?
-    int result = sysWriteFully(outFd, headCtx.file_data_ptr_, headCtx.file_data_size_, "hprof-head");
-    result |= sysWriteFully(outFd, file_data_ptr_, file_data_size_, "hprof-tail");
-    close(outFd);
-    if (result != 0) {
-      // TODO: better detail message.
-      Thread::Current()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", "Couldn't dump heap; check log output for details");
-      return;
-    }
-  }
-
-  // throw out a log message for the benefit of "runhat"
-  LOG(INFO) << "hprof: heap dump completed (" << PrettySize(headCtx.file_data_size_ + file_data_size_ + 1023) << ")";
-}
-
-Hprof::~Hprof() {
-  // we don't own ctx->fd_, do not close
-  if (mem_fp_ != NULL) {
-    fclose(mem_fp_);
-  }
-  free(current_record_.body_);
-  free(file_data_ptr_);
-}
-
 void Hprof::VisitRoot(const Object* obj) {
   uint32_t threadId = 0;  // TODO
-  /*RootType */ size_t type = 0; // TODO
+  /*RootType*/ size_t type = 0; // TODO
 
   static const HprofHeapTag xlate[] = {
     HPROF_ROOT_UNKNOWN,
@@ -957,137 +1046,20 @@
   gc_thread_serial_number_ = 0;
 }
 
-HprofStringId Hprof::LookupStringId(String* string) {
-  return LookupStringId(string->ToModifiedUtf8());
-}
-
-HprofStringId Hprof::LookupStringId(const char* string) {
-  return LookupStringId(std::string(string));
-}
-
-HprofStringId Hprof::LookupStringId(const std::string& string) {
-  StringMapIterator it = strings_.find(string);
-  if (it != strings_.end()) {
-    return it->second;
-  }
-  HprofStringId id = next_string_id_++;
-  strings_.Put(string, id);
-  return id;
-}
-
-int Hprof::DumpStrings() {
-  HprofRecord *rec = &current_record_;
-
-  for (StringMapIterator it = strings_.begin(); it != strings_.end(); ++it) {
-    std::string string((*it).first);
-    size_t id = (*it).second;
-
-    int err = StartNewRecord(HPROF_TAG_STRING, HPROF_TIME);
-    if (err != 0) {
-      return err;
-    }
-
-    // STRING format:
-    // ID:  ID for this string
-    // U1*: UTF8 characters for string (NOT NULL terminated)
-    //      (the record format encodes the length)
-    err = rec->AddU4(id);
-    if (err != 0) {
-      return err;
-    }
-    err = rec->AddUtf8String(string.c_str());
-    if (err != 0) {
-      return err;
-    }
-  }
-
-  return 0;
-}
-
-HprofStringId Hprof::LookupClassNameId(const Class* c) {
-  return LookupStringId(PrettyDescriptor(c));
-}
-
-HprofClassObjectId Hprof::LookupClassId(const Class* c) {
-  if (c == NULL) {
-    // c is the superclass of java.lang.Object or a primitive
-    return (HprofClassObjectId)0;
-  }
-
-  std::pair<ClassSetIterator, bool> result = classes_.insert(c);
-  const Class* present = *result.first;
-
-  // Make sure that we've assigned a string ID for this class' name
-  LookupClassNameId(c);
-
-  CHECK_EQ(present, c);
-  return (HprofStringId) present;
-}
-
-int Hprof::DumpClasses() {
-  HprofRecord *rec = &current_record_;
-  uint32_t nextSerialNumber = 1;
-
-  for (ClassSetIterator it = classes_.begin(); it != classes_.end(); ++it) {
-    const Class* c = *it;
-    CHECK(c != NULL);
-
-    int err = StartNewRecord(HPROF_TAG_LOAD_CLASS, HPROF_TIME);
-    if (err != 0) {
-      return err;
-    }
-
-    // LOAD CLASS format:
-    // U4: class serial number (always > 0)
-    // ID: class object ID. We use the address of the class object structure as its ID.
-    // U4: stack trace serial number
-    // ID: class name string ID
-    rec->AddU4(nextSerialNumber++);
-    rec->AddId((HprofClassObjectId) c);
-    rec->AddU4(HPROF_NULL_STACK_TRACE);
-    rec->AddId(LookupClassNameId(c));
-  }
-
-  return 0;
-}
-
-static void HprofRootVisitor(const Object* obj, void* arg) {
-  CHECK(arg != NULL);
-  Hprof* hprof = reinterpret_cast<Hprof*>(arg);
-  hprof->VisitRoot(obj);
-}
-
-static void HprofBitmapCallback(Object *obj, void *arg) {
-  CHECK(obj != NULL);
-  CHECK(arg != NULL);
-  Hprof* hprof = reinterpret_cast<Hprof*>(arg);
-  hprof->DumpHeapObject(obj);
-}
-
-/*
- * Walk the roots and heap writing heap information to the specified
- * file.
- *
- * If "fd" is >= 0, the output will be written to that file descriptor.
- * Otherwise, "file_name_" is used to create an output file.
- *
- * If "direct_to_ddms_" is set, the other arguments are ignored, and data is
- * sent directly to DDMS.
- */
-void DumpHeap(const char* fileName, int fd, bool direct_to_ddms) {
-  CHECK(fileName != NULL);
+// 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 != NULL);
   ScopedHeapLock heap_lock;
   ScopedThreadStateChange tsc(Thread::Current(), kRunnable);
 
   ThreadList* thread_list = Runtime::Current()->GetThreadList();
   thread_list->SuspendAll();
 
-  Runtime* runtime = Runtime::Current();
-  Hprof hprof(fileName, fd, false, direct_to_ddms);
-  runtime->VisitRoots(HprofRootVisitor, &hprof);
-  runtime->GetHeap()->GetLiveBits()->Walk(HprofBitmapCallback, &hprof);
-  // TODO: write a HEAP_SUMMARY record
-  hprof.Finish();
+  Hprof hprof(filename, fd, direct_to_ddms);
+  hprof.Dump();
   thread_list->ResumeAll();
 }