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 = ¤t_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 = ¤t_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 = ¤t_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 = ¤t_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 = ¤t_record_;
+ HprofRecord* rec = ¤t_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 = ¤t_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 = ¤t_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();
}