Divide up hprof code by classes.
This divides the _class, _heap, _output and _string files
among hprof.cc and hprof_record.cc.
This removes the use of mutex locks on class and string
collections. This does not otherwise change the body of any
functions, just which files they live in.
Change-Id: I88521529dbe3e00bd88ec0030a6105dceda9500d
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 7ded084..968228b 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -122,10 +122,7 @@
src/heap.cc \
src/heap_bitmap.cc \
src/hprof/hprof.cc \
- src/hprof/hprof_class.cc \
- src/hprof/hprof_heap.cc \
- src/hprof/hprof_output.cc \
- src/hprof/hprof_string.cc \
+ src/hprof/hprof_record.cc \
src/image.cc \
src/image_writer.cc \
src/indirect_reference_table.cc \
diff --git a/src/hprof/hprof.cc b/src/hprof/hprof.cc
index d00f6f9..db1b637 100644
--- a/src/hprof/hprof.cc
+++ b/src/hprof/hprof.cc
@@ -25,10 +25,13 @@
#include "hprof.h"
#include "heap.h"
#include "debugger.h"
+#include "object.h"
#include "stringprintf.h"
-#include "thread_list.h"
+#include "unordered_map.h"
+#include "unordered_set.h"
#include "logging.h"
+#include <cutils/open_memstream.h>
#include <sys/uio.h>
#include <string.h>
#include <unistd.h>
@@ -41,6 +44,459 @@
namespace hprof {
+#define HPROF_MAGIC_STRING "JAVA PROFILE 1.0.3"
+
+/*
+ * Initialize an Hprof.
+ */
+Hprof::Hprof(const char *outputFileName, int fd, bool writeHeader, bool directToDdms)
+ : 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_(NULL),
+ file_data_ptr_(NULL),
+ file_data_size_(0),
+ mem_fp_(NULL),
+ fd_(0),
+ next_string_id_(0x400000) {
+ // 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
+ LOG(ERROR) << StringPrintf("hprof: open_memstream failed: %s", strerror(errno));
+ CHECK(false);
+ }
+
+ direct_to_ddms_ = directToDdms;
+ file_name_ = strdup(outputFileName);
+ mem_fp_ = fp;
+ fd_ = fd;
+
+ current_record_.alloc_length_ = 128;
+ current_record_.body_ = (unsigned char *)malloc(current_record_.alloc_length_);
+ // TODO check for/return an error
+
+ if (writeHeader) {
+ char magic[] = HPROF_MAGIC_STRING;
+ unsigned char buf[4];
+
+ // Write the file header.
+ // U1: NUL-terminated magic string.
+ fwrite(magic, 1, sizeof(magic), 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);
+
+ // The current time, in milliseconds since 0:00 GMT, 1/1/70.
+ struct timeval now;
+ uint64_t nowMs;
+ if (gettimeofday(&now, NULL) < 0) {
+ nowMs = 0;
+ } else {
+ nowMs = (uint64_t)now.tv_sec * 1000 + now.tv_usec / 1000;
+ }
+
+ // 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);
+
+ // 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();
+ }
+
+ rec->dirty_ = true;
+ rec->tag_ = tag;
+ rec->time_ = time;
+ rec->length_ = 0;
+ return 0;
+}
+
+int Hprof::FlushCurrentRecord() {
+ return current_record_.Flush(mem_fp_);
+}
+
+// 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
+
+#define OBJECTS_PER_SEGMENT ((size_t)128)
+#define BYTES_PER_SEGMENT ((size_t)4096)
+
+// The static field-name for the synthetic object generated to account
+// for class static overhead.
+#define STATIC_OVERHEAD_NAME "$staticOverhead"
+// The ID for the synthetic object generated to account for class static overhead.
+#define CLASS_STATICS_ID(clazz) ((HprofObjectId)(((uint32_t)(clazz)) | 1))
+
+HprofBasicType Hprof::SignatureToBasicTypeAndSize(const char *sig, size_t *sizeOut) {
+ char c = sig[0];
+ HprofBasicType ret;
+ size_t size;
+
+ switch (c) {
+ case '[':
+ case 'L': ret = hprof_basic_object; size = 4; break;
+ case 'Z': ret = hprof_basic_boolean; size = 1; break;
+ case 'C': ret = hprof_basic_char; size = 2; break;
+ case 'F': ret = hprof_basic_float; size = 4; break;
+ case 'D': ret = hprof_basic_double; size = 8; break;
+ case 'B': ret = hprof_basic_byte; size = 1; break;
+ case 'S': ret = hprof_basic_short; size = 2; break;
+ default: CHECK(false);
+ case 'I': ret = hprof_basic_int; size = 4; break;
+ case 'J': ret = hprof_basic_long; size = 8; break;
+ }
+
+ if (sizeOut != NULL) {
+ *sizeOut = size;
+ }
+
+ return ret;
+}
+
+HprofBasicType Hprof::PrimitiveToBasicTypeAndSize(Primitive::Type prim, size_t *sizeOut) {
+ HprofBasicType ret;
+ size_t size;
+
+ switch (prim) {
+ case Primitive::kPrimBoolean: ret = hprof_basic_boolean; size = 1; break;
+ case Primitive::kPrimChar: ret = hprof_basic_char; size = 2; break;
+ case Primitive::kPrimFloat: ret = hprof_basic_float; size = 4; break;
+ case Primitive::kPrimDouble: ret = hprof_basic_double; size = 8; break;
+ case Primitive::kPrimByte: ret = hprof_basic_byte; size = 1; break;
+ case Primitive::kPrimShort: ret = hprof_basic_short; size = 2; break;
+ default: CHECK(false);
+ case Primitive::kPrimInt: ret = hprof_basic_int; size = 4; break;
+ case Primitive::kPrimLong: ret = hprof_basic_long; size = 8; break;
+ }
+
+ if (sizeOut != NULL) {
+ *sizeOut = size;
+ }
+
+ return ret;
+}
+
+// Always called when marking objects, but only does
+// 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) {
+ HprofRecord *rec = ¤t_record_;
+ int err; // TODO: we may return this uninitialized
+ HprofHeapTag heapTag = (HprofHeapTag)gc_scan_state_;
+
+ if (heapTag == 0) {
+ 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;
+ }
+
+ switch (heapTag) {
+ // 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_FINALIZING:
+ case HPROF_ROOT_DEBUGGER:
+ case HPROF_ROOT_REFERENCE_CLEANUP:
+ case HPROF_ROOT_VM_INTERNAL:
+ rec->AddU1(heapTag);
+ rec->AddId((HprofObjectId)obj);
+ break;
+
+ // ID: object ID
+ // ID: JNI global ref ID
+ case HPROF_ROOT_JNI_GLOBAL:
+ rec->AddU1(heapTag);
+ rec->AddId((HprofObjectId)obj);
+ rec->AddId((HprofId)jniObj);
+ break;
+
+ // ID: object ID
+ // U4: thread serial number
+ // U4: frame number in stack trace (-1 for empty)
+ case HPROF_ROOT_JNI_LOCAL:
+ case HPROF_ROOT_JNI_MONITOR:
+ case HPROF_ROOT_JAVA_FRAME:
+ rec->AddU1(heapTag);
+ rec->AddId((HprofObjectId)obj);
+ rec->AddU4(gc_thread_serial_number_);
+ rec->AddU4((uint32_t)-1);
+ break;
+
+ // ID: object ID
+ // U4: thread serial number
+ case HPROF_ROOT_NATIVE_STACK:
+ case HPROF_ROOT_THREAD_BLOCK:
+ rec->AddU1(heapTag);
+ rec->AddId((HprofObjectId)obj);
+ rec->AddU4(gc_thread_serial_number_);
+ break;
+
+ // ID: thread object ID
+ // U4: thread serial number
+ // U4: stack trace serial number
+ case HPROF_ROOT_THREAD_OBJECT:
+ rec->AddU1(heapTag);
+ rec->AddId((HprofObjectId)obj);
+ rec->AddU4(gc_thread_serial_number_);
+ rec->AddU4((uint32_t)-1); //xxx
+ break;
+
+ default:
+ err = 0;
+ break;
+ }
+
+ objects_in_segment_++;
+ return err;
+}
+
+int Hprof::StackTraceSerialNumber(const void *obj) {
+ return HPROF_NULL_STACK_TRACE;
+}
+
+int Hprof::DumpHeapObject(const Object* obj) {
+ 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 (desiredHeap != current_heap_) {
+ HprofStringId nameId;
+
+ // This object is in a different heap than the current one.
+ // Emit a HEAP_DUMP_INFO tag to change heaps.
+ rec->AddU1(HPROF_HEAP_DUMP_INFO);
+ rec->AddU4((uint32_t)desiredHeap); // uint32_t: heap id
+ switch (desiredHeap) {
+ case HPROF_HEAP_APP:
+ nameId = LookupStringId("app");
+ break;
+ case HPROF_HEAP_ZYGOTE:
+ nameId = LookupStringId("zygote");
+ break;
+ default:
+ // Internal error
+ LOG(ERROR) << "Unexpected desiredHeap";
+ nameId = LookupStringId("<ILLEGAL>");
+ break;
+ }
+ rec->AddId(nameId);
+ current_heap_ = desiredHeap;
+ }
+
+ Class* clazz = obj->GetClass();
+ if (clazz == NULL) {
+ // 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()) {
+ Class* thisClass = (Class*)obj;
+ // obj is a ClassObject.
+ size_t sFieldCount = thisClass->NumStaticFields();
+ if (sFieldCount != 0) {
+ int byteLength = sFieldCount*sizeof(JValue); // TODO bogus; fields are packed
+ // Create a byte array to reflect the allocation of the
+ // StaticField array at the end of this class.
+ rec->AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
+ rec->AddId(CLASS_STATICS_ID(obj));
+ rec->AddU4(StackTraceSerialNumber(obj));
+ rec->AddU4(byteLength);
+ rec->AddU1(hprof_basic_byte);
+ for (int i = 0; i < byteLength; i++) {
+ rec->AddU1(0);
+ }
+ }
+
+ rec->AddU1(HPROF_CLASS_DUMP);
+ rec->AddId(LookupClassId(thisClass));
+ rec->AddU4(StackTraceSerialNumber(thisClass));
+ rec->AddId(LookupClassId(thisClass->GetSuperClass()));
+ rec->AddId((HprofObjectId)thisClass->GetClassLoader());
+ rec->AddId((HprofObjectId)0); // no signer
+ rec->AddId((HprofObjectId)0); // no prot domain
+ rec->AddId((HprofId)0); // reserved
+ rec->AddId((HprofId)0); // reserved
+ if (obj->IsClassClass()) {
+ // ClassObjects have their static fields appended, so aren't all the same size.
+ // But they're at least this size.
+ rec->AddU4(sizeof(Class)); // instance size
+ } else if (thisClass->IsArrayClass() || thisClass->IsPrimitive()) {
+ rec->AddU4(0);
+ } else {
+ rec->AddU4(thisClass->GetObjectSize()); // instance size
+ }
+
+ rec->AddU2(0); // empty const pool
+
+ // Static fields
+ if (sFieldCount == 0) {
+ rec->AddU2((uint16_t)0);
+ } else {
+ rec->AddU2((uint16_t)(sFieldCount+1));
+ rec->AddId(LookupStringId(STATIC_OVERHEAD_NAME));
+ rec->AddU1(hprof_basic_object);
+ rec->AddId(CLASS_STATICS_ID(obj));
+
+ for (size_t i = 0; i < sFieldCount; ++i) {
+ Field* f = thisClass->GetStaticField(i);
+
+ size_t size;
+ HprofBasicType t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size);
+ rec->AddId(LookupStringId(f->GetName()));
+ rec->AddU1(t);
+ if (size == 1) {
+ rec->AddU1(static_cast<uint8_t>(f->Get32(NULL)));
+ } else if (size == 2) {
+ rec->AddU2(static_cast<uint16_t>(f->Get32(NULL)));
+ } else if (size == 4) {
+ rec->AddU4(f->Get32(NULL));
+ } else if (size == 8) {
+ rec->AddU8(f->Get64(NULL));
+ } else {
+ CHECK(false);
+ }
+ }
+ }
+
+ // Instance fields for this class (no superclass fields)
+ int iFieldCount = thisClass->IsObjectClass() ? 0 : thisClass->NumInstanceFields();
+ rec->AddU2((uint16_t)iFieldCount);
+ for (int i = 0; i < iFieldCount; ++i) {
+ Field* f = thisClass->GetInstanceField(i);
+ HprofBasicType t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), NULL);
+ rec->AddId(LookupStringId(f->GetName()));
+ rec->AddU1(t);
+ }
+ } else if (clazz->IsArrayClass()) {
+ Array *aobj = (Array *)obj;
+ uint32_t length = aobj->GetLength();
+
+ if (obj->IsObjectArray()) {
+ // obj is an object array.
+ rec->AddU1(HPROF_OBJECT_ARRAY_DUMP);
+
+ rec->AddId((HprofObjectId)obj);
+ rec->AddU4(StackTraceSerialNumber(obj));
+ rec->AddU4(length);
+ rec->AddId(LookupClassId(clazz));
+
+ // Dump the elements, which are always objects or NULL.
+ rec->AddIdList((const HprofObjectId *)aobj->GetRawData(), length);
+ } else {
+ size_t size;
+ HprofBasicType t = PrimitiveToBasicTypeAndSize(clazz->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(), length);
+ } else if (size == 2) {
+ rec->AddU2List((const uint16_t *)(void *)aobj->GetRawData(), length);
+ } else if (size == 4) {
+ rec->AddU4List((const uint32_t *)(void *)aobj->GetRawData(), length);
+ } else if (size == 8) {
+ rec->AddU8List((const uint64_t *)aobj->GetRawData(), length);
+ }
+#endif
+ }
+ } else {
+
+ // obj is an instance object.
+ rec->AddU1(HPROF_INSTANCE_DUMP);
+ rec->AddId((HprofObjectId)obj);
+ rec->AddU4(StackTraceSerialNumber(obj));
+ rec->AddId(LookupClassId(clazz));
+
+ // 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_;
+ rec->AddU4(0x77777777);
+
+ // Write the instance data; fields for this class, followed by super class fields,
+ // and so on. Don't write the klass or monitor fields of Object.class.
+ const Class* sclass = clazz;
+ while (!sclass->IsObjectClass()) {
+ int ifieldCount = sclass->NumInstanceFields();
+ for (int i = 0; i < ifieldCount; i++) {
+ Field* f = sclass->GetInstanceField(i);
+ size_t size;
+ SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size);
+ if (size == 1) {
+ rec->AddU1(f->Get32(obj));
+ } else if (size == 2) {
+ rec->AddU2(f->Get32(obj));
+ } else if (size == 4) {
+ rec->AddU4(f->Get32(obj));
+ } else if (size == 8) {
+ rec->AddU8(f->Get64(obj));
+ } else {
+ CHECK(false);
+ }
+ }
+
+ sclass = sclass->GetSuperClass();
+ }
+
+ // Patch the instance field length.
+ size_t savedLen = rec->length_;
+ rec->length_ = sizePatchOffset;
+ rec->AddU4(savedLen - (sizePatchOffset + 4));
+ rec->length_ = savedLen;
+ }
+ }
+
+ objects_in_segment_++;
+ return 0;
+}
+
#define kHeadSuffix "-hptemp"
// TODO: use File::WriteFully
@@ -146,15 +602,6 @@
free(file_data_ptr_);
}
-/*
- * Visitor invoked on every root reference.
- */
-void HprofRootVisitor(const Object* obj, void* arg) {
- CHECK(arg != NULL);
- Hprof* hprof = (Hprof*)arg;
- hprof->VisitRoot(obj);
-}
-
void Hprof::VisitRoot(const Object* obj) {
uint32_t threadId = 0; // TODO
/*RootType */ size_t type = 0; // TODO
@@ -188,10 +635,104 @@
gc_thread_serial_number_ = 0;
}
-/*
- * Visitor invoked on every heap object.
- */
-static void HprofBitmapCallback(Object *obj, void *arg) {
+HprofStringId Hprof::LookupStringId(String* string) {
+ return LookupStringId(string->ToModifiedUtf8());
+}
+
+HprofStringId Hprof::LookupStringId(const char* string) {
+ return LookupStringId(std::string(string));
+}
+
+HprofStringId Hprof::LookupStringId(std::string string) {
+ if (strings_.find(string) == strings_.end()) {
+ strings_[string] = next_string_id_++;
+ }
+ return strings_[string];
+}
+
+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(Class* clazz) {
+ return LookupStringId(PrettyDescriptor(clazz->GetDescriptor()));
+}
+
+HprofClassObjectId Hprof::LookupClassId(Class* clazz) {
+ if (clazz == NULL) {
+ // clazz is the superclass of java.lang.Object or a primitive
+ return (HprofClassObjectId)0;
+ }
+
+ std::pair<ClassSetIterator, bool> result = classes_.insert(clazz);
+ Class* present = *result.first;
+
+ // Make sure that we've assigned a string ID for this class' name
+ LookupClassNameId(clazz);
+
+ CHECK_EQ(present, clazz);
+ return (HprofStringId) present;
+}
+
+int Hprof::DumpClasses() {
+ HprofRecord *rec = ¤t_record_;
+ uint32_t nextSerialNumber = 1;
+
+ for (ClassSetIterator it = classes_.begin(); it != classes_.end(); ++it) {
+ Class* clazz = *it;
+ CHECK(clazz != 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) clazz);
+ rec->AddU4(HPROF_NULL_STACK_TRACE);
+ rec->AddId(LookupClassNameId(clazz));
+ }
+
+ return 0;
+}
+
+void HprofRootVisitor(const Object* obj, void* arg) {
+ CHECK(arg != NULL);
+ Hprof* hprof = (Hprof*)arg;
+ hprof->VisitRoot(obj);
+}
+
+void HprofBitmapCallback(Object *obj, void *arg) {
CHECK(obj != NULL);
CHECK(arg != NULL);
Hprof *hprof = (Hprof*)arg;
diff --git a/src/hprof/hprof.h b/src/hprof/hprof.h
index 38520cc..7378ec9 100644
--- a/src/hprof/hprof.h
+++ b/src/hprof/hprof.h
@@ -22,6 +22,7 @@
#include "object.h"
#include "unordered_map.h"
#include "unordered_set.h"
+#include "thread_list.h"
namespace art {
@@ -36,6 +37,41 @@
#define HPROF_NULL_STACK_TRACE 0
#define HPROF_NULL_THREAD 0
+#define U2_TO_BUF_BE(buf, offset, value) \
+ do { \
+ unsigned char *buf_ = (unsigned char *)(buf); \
+ int offset_ = (int)(offset); \
+ uint16_t value_ = (uint16_t)(value); \
+ buf_[offset_ + 0] = (unsigned char)(value_ >> 8); \
+ buf_[offset_ + 1] = (unsigned char)(value_ ); \
+ } while (0)
+
+#define U4_TO_BUF_BE(buf, offset, value) \
+ do { \
+ unsigned char *buf_ = (unsigned char *)(buf); \
+ int offset_ = (int)(offset); \
+ uint32_t value_ = (uint32_t)(value); \
+ buf_[offset_ + 0] = (unsigned char)(value_ >> 24); \
+ buf_[offset_ + 1] = (unsigned char)(value_ >> 16); \
+ buf_[offset_ + 2] = (unsigned char)(value_ >> 8); \
+ buf_[offset_ + 3] = (unsigned char)(value_ ); \
+ } while (0)
+
+#define U8_TO_BUF_BE(buf, offset, value) \
+ do { \
+ unsigned char *buf_ = (unsigned char *)(buf); \
+ int offset_ = (int)(offset); \
+ uint64_t value_ = (uint64_t)(value); \
+ buf_[offset_ + 0] = (unsigned char)(value_ >> 56); \
+ buf_[offset_ + 1] = (unsigned char)(value_ >> 48); \
+ buf_[offset_ + 2] = (unsigned char)(value_ >> 40); \
+ buf_[offset_ + 3] = (unsigned char)(value_ >> 32); \
+ buf_[offset_ + 4] = (unsigned char)(value_ >> 24); \
+ buf_[offset_ + 5] = (unsigned char)(value_ >> 16); \
+ buf_[offset_ + 6] = (unsigned char)(value_ >> 8); \
+ buf_[offset_ + 7] = (unsigned char)(value_ ); \
+ } while (0)
+
typedef uint32_t HprofId;
typedef HprofId HprofStringId;
typedef HprofId HprofObjectId;
@@ -129,6 +165,9 @@
size_t alloc_length_;
uint8_t tag_;
bool dirty_;
+
+ private:
+ int GuaranteeRecordAppend(size_t nmore);
};
enum HprofHeapId {
@@ -157,6 +196,9 @@
HprofStringId LookupStringId(const char* string);
HprofStringId LookupStringId(std::string string);
HprofStringId LookupClassNameId(Class* clazz);
+ static HprofBasicType SignatureToBasicTypeAndSize(const char *sig, size_t *sizeOut);
+ static HprofBasicType PrimitiveToBasicTypeAndSize(Primitive::Type prim, size_t *sizeOut);
+ static int StackTraceSerialNumber(const void *obj);
// current_record_ *must* be first so that we can cast from a context to a record.
HprofRecord current_record_;
@@ -176,11 +218,8 @@
FILE *mem_fp_;
int fd_;
- mutable Mutex classes_lock_;
ClassSet classes_;
-
size_t next_string_id_;
- mutable Mutex strings_lock_;
StringMap strings_;
};
diff --git a/src/hprof/hprof_class.cc b/src/hprof/hprof_class.cc
deleted file mode 100644
index 4286d29..0000000
--- a/src/hprof/hprof_class.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-/*
- * Class object pool
- */
-
-#include "hprof.h"
-#include "object.h"
-#include "logging.h"
-#include "unordered_set.h"
-
-namespace art {
-
-namespace hprof {
-
-HprofStringId Hprof::LookupClassNameId(Class* clazz) {
- return LookupStringId(PrettyDescriptor(clazz->GetDescriptor()));
-}
-
-HprofClassObjectId Hprof::LookupClassId(Class* clazz) {
- if (clazz == NULL) {
- // clazz is the superclass of java.lang.Object or a primitive
- return (HprofClassObjectId)0;
- }
-
- MutexLock mu(classes_lock_);
-
- std::pair<ClassSetIterator, bool> result = classes_.insert(clazz);
- Class* present = *result.first;
-
- // Make sure that we've assigned a string ID for this class' name
- LookupClassNameId(clazz);
-
- CHECK_EQ(present, clazz);
- return (HprofStringId) present;
-}
-
-int Hprof::DumpClasses() {
- MutexLock mu(classes_lock_);
- HprofRecord *rec = ¤t_record_;
- uint32_t nextSerialNumber = 1;
-
- for (ClassSetIterator it = classes_.begin(); it != classes_.end(); ++it) {
- Class* clazz = *it;
- CHECK(clazz != 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) clazz);
- rec->AddU4(HPROF_NULL_STACK_TRACE);
- rec->AddId(LookupClassNameId(clazz));
- }
-
- return 0;
-}
-
-} // namespace hprof
-
-} // namespace art
diff --git a/src/hprof/hprof_heap.cc b/src/hprof/hprof_heap.cc
deleted file mode 100644
index 0b63e65..0000000
--- a/src/hprof/hprof_heap.cc
+++ /dev/null
@@ -1,394 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-/*
- * Heap object dump
- */
-#include "hprof.h"
-#include "primitive.h"
-#include "object.h"
-#include "logging.h"
-
-namespace art {
-
-namespace hprof {
-
-// 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
-
-#define OBJECTS_PER_SEGMENT ((size_t)128)
-#define BYTES_PER_SEGMENT ((size_t)4096)
-
-// The static field-name for the synthetic object generated to account
-// for class static overhead.
-#define STATIC_OVERHEAD_NAME "$staticOverhead"
-// The ID for the synthetic object generated to account for class static overhead.
-#define CLASS_STATICS_ID(clazz) ((HprofObjectId)(((uint32_t)(clazz)) | 1))
-
-static HprofBasicType signatureToBasicTypeAndSize(const char *sig, size_t *sizeOut) {
- char c = sig[0];
- HprofBasicType ret;
- size_t size;
-
- switch (c) {
- case '[':
- case 'L': ret = hprof_basic_object; size = 4; break;
- case 'Z': ret = hprof_basic_boolean; size = 1; break;
- case 'C': ret = hprof_basic_char; size = 2; break;
- case 'F': ret = hprof_basic_float; size = 4; break;
- case 'D': ret = hprof_basic_double; size = 8; break;
- case 'B': ret = hprof_basic_byte; size = 1; break;
- case 'S': ret = hprof_basic_short; size = 2; break;
- default: CHECK(false);
- case 'I': ret = hprof_basic_int; size = 4; break;
- case 'J': ret = hprof_basic_long; size = 8; break;
- }
-
- if (sizeOut != NULL) {
- *sizeOut = size;
- }
-
- return ret;
-}
-
-static HprofBasicType primitiveToBasicTypeAndSize(Primitive::Type prim, size_t *sizeOut) {
- HprofBasicType ret;
- size_t size;
-
- switch (prim) {
- case Primitive::kPrimBoolean: ret = hprof_basic_boolean; size = 1; break;
- case Primitive::kPrimChar: ret = hprof_basic_char; size = 2; break;
- case Primitive::kPrimFloat: ret = hprof_basic_float; size = 4; break;
- case Primitive::kPrimDouble: ret = hprof_basic_double; size = 8; break;
- case Primitive::kPrimByte: ret = hprof_basic_byte; size = 1; break;
- case Primitive::kPrimShort: ret = hprof_basic_short; size = 2; break;
- default: CHECK(false);
- case Primitive::kPrimInt: ret = hprof_basic_int; size = 4; break;
- case Primitive::kPrimLong: ret = hprof_basic_long; size = 8; break;
- }
-
- if (sizeOut != NULL) {
- *sizeOut = size;
- }
-
- return ret;
-}
-
-// Always called when marking objects, but only does
-// 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) {
- HprofRecord *rec = ¤t_record_;
- int err; // TODO: we may return this uninitialized
- HprofHeapTag heapTag = (HprofHeapTag)gc_scan_state_;
-
- if (heapTag == 0) {
- 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;
- }
-
- switch (heapTag) {
- // 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_FINALIZING:
- case HPROF_ROOT_DEBUGGER:
- case HPROF_ROOT_REFERENCE_CLEANUP:
- case HPROF_ROOT_VM_INTERNAL:
- rec->AddU1(heapTag);
- rec->AddId((HprofObjectId)obj);
- break;
-
- // ID: object ID
- // ID: JNI global ref ID
- case HPROF_ROOT_JNI_GLOBAL:
- rec->AddU1(heapTag);
- rec->AddId((HprofObjectId)obj);
- rec->AddId((HprofId)jniObj);
- break;
-
- // ID: object ID
- // U4: thread serial number
- // U4: frame number in stack trace (-1 for empty)
- case HPROF_ROOT_JNI_LOCAL:
- case HPROF_ROOT_JNI_MONITOR:
- case HPROF_ROOT_JAVA_FRAME:
- rec->AddU1(heapTag);
- rec->AddId((HprofObjectId)obj);
- rec->AddU4(gc_thread_serial_number_);
- rec->AddU4((uint32_t)-1);
- break;
-
- // ID: object ID
- // U4: thread serial number
- case HPROF_ROOT_NATIVE_STACK:
- case HPROF_ROOT_THREAD_BLOCK:
- rec->AddU1(heapTag);
- rec->AddId((HprofObjectId)obj);
- rec->AddU4(gc_thread_serial_number_);
- break;
-
- // ID: thread object ID
- // U4: thread serial number
- // U4: stack trace serial number
- case HPROF_ROOT_THREAD_OBJECT:
- rec->AddU1(heapTag);
- rec->AddId((HprofObjectId)obj);
- rec->AddU4(gc_thread_serial_number_);
- rec->AddU4((uint32_t)-1); //xxx
- break;
-
- default:
- err = 0;
- break;
- }
-
- objects_in_segment_++;
- return err;
-}
-
-static int stackTraceSerialNumber(const void *obj) {
- return HPROF_NULL_STACK_TRACE;
-}
-
-int Hprof::DumpHeapObject(const Object* obj) {
- 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 (desiredHeap != current_heap_) {
- HprofStringId nameId;
-
- // This object is in a different heap than the current one.
- // Emit a HEAP_DUMP_INFO tag to change heaps.
- rec->AddU1(HPROF_HEAP_DUMP_INFO);
- rec->AddU4((uint32_t)desiredHeap); // uint32_t: heap id
- switch (desiredHeap) {
- case HPROF_HEAP_APP:
- nameId = LookupStringId("app");
- break;
- case HPROF_HEAP_ZYGOTE:
- nameId = LookupStringId("zygote");
- break;
- default:
- // Internal error
- LOG(ERROR) << "Unexpected desiredHeap";
- nameId = LookupStringId("<ILLEGAL>");
- break;
- }
- rec->AddId(nameId);
- current_heap_ = desiredHeap;
- }
-
- Class* clazz = obj->GetClass();
- if (clazz == NULL) {
- // 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()) {
- Class* thisClass = (Class*)obj;
- // obj is a ClassObject.
- size_t sFieldCount = thisClass->NumStaticFields();
- if (sFieldCount != 0) {
- int byteLength = sFieldCount*sizeof(JValue); // TODO bogus; fields are packed
- // Create a byte array to reflect the allocation of the
- // StaticField array at the end of this class.
- rec->AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
- rec->AddId(CLASS_STATICS_ID(obj));
- rec->AddU4(stackTraceSerialNumber(obj));
- rec->AddU4(byteLength);
- rec->AddU1(hprof_basic_byte);
- for (int i = 0; i < byteLength; i++) {
- rec->AddU1(0);
- }
- }
-
- rec->AddU1(HPROF_CLASS_DUMP);
- rec->AddId(LookupClassId(thisClass));
- rec->AddU4(stackTraceSerialNumber(thisClass));
- rec->AddId(LookupClassId(thisClass->GetSuperClass()));
- rec->AddId((HprofObjectId)thisClass->GetClassLoader());
- rec->AddId((HprofObjectId)0); // no signer
- rec->AddId((HprofObjectId)0); // no prot domain
- rec->AddId((HprofId)0); // reserved
- rec->AddId((HprofId)0); // reserved
- if (obj->IsClassClass()) {
- // ClassObjects have their static fields appended, so aren't all the same size.
- // But they're at least this size.
- rec->AddU4(sizeof(Class)); // instance size
- } else if (thisClass->IsArrayClass() || thisClass->IsPrimitive()) {
- rec->AddU4(0);
- } else {
- rec->AddU4(thisClass->GetObjectSize()); // instance size
- }
-
- rec->AddU2(0); // empty const pool
-
- // Static fields
- if (sFieldCount == 0) {
- rec->AddU2((uint16_t)0);
- } else {
- rec->AddU2((uint16_t)(sFieldCount+1));
- rec->AddId(LookupStringId(STATIC_OVERHEAD_NAME));
- rec->AddU1(hprof_basic_object);
- rec->AddId(CLASS_STATICS_ID(obj));
-
- for (size_t i = 0; i < sFieldCount; ++i) {
- Field* f = thisClass->GetStaticField(i);
-
- size_t size;
- HprofBasicType t = signatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size);
- rec->AddId(LookupStringId(f->GetName()));
- rec->AddU1(t);
- if (size == 1) {
- rec->AddU1(static_cast<uint8_t>(f->Get32(NULL)));
- } else if (size == 2) {
- rec->AddU2(static_cast<uint16_t>(f->Get32(NULL)));
- } else if (size == 4) {
- rec->AddU4(f->Get32(NULL));
- } else if (size == 8) {
- rec->AddU8(f->Get64(NULL));
- } else {
- CHECK(false);
- }
- }
- }
-
- // Instance fields for this class (no superclass fields)
- int iFieldCount = thisClass->IsObjectClass() ? 0 : thisClass->NumInstanceFields();
- rec->AddU2((uint16_t)iFieldCount);
- for (int i = 0; i < iFieldCount; ++i) {
- Field* f = thisClass->GetInstanceField(i);
- HprofBasicType t = signatureToBasicTypeAndSize(f->GetTypeDescriptor(), NULL);
- rec->AddId(LookupStringId(f->GetName()));
- rec->AddU1(t);
- }
- } else if (clazz->IsArrayClass()) {
- Array *aobj = (Array *)obj;
- uint32_t length = aobj->GetLength();
-
- if (obj->IsObjectArray()) {
- // obj is an object array.
- rec->AddU1(HPROF_OBJECT_ARRAY_DUMP);
-
- rec->AddId((HprofObjectId)obj);
- rec->AddU4(stackTraceSerialNumber(obj));
- rec->AddU4(length);
- rec->AddId(LookupClassId(clazz));
-
- // Dump the elements, which are always objects or NULL.
- rec->AddIdList((const HprofObjectId *)aobj->GetRawData(), length);
- } else {
- size_t size;
- HprofBasicType t = primitiveToBasicTypeAndSize(clazz->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(), length);
- } else if (size == 2) {
- rec->AddU2List((const uint16_t *)(void *)aobj->GetRawData(), length);
- } else if (size == 4) {
- rec->AddU4List((const uint32_t *)(void *)aobj->GetRawData(), length);
- } else if (size == 8) {
- rec->AddU8List((const uint64_t *)aobj->GetRawData(), length);
- }
-#endif
- }
- } else {
-
- // obj is an instance object.
- rec->AddU1(HPROF_INSTANCE_DUMP);
- rec->AddId((HprofObjectId)obj);
- rec->AddU4(stackTraceSerialNumber(obj));
- rec->AddId(LookupClassId(clazz));
-
- // 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_;
- rec->AddU4(0x77777777);
-
- // Write the instance data; fields for this class, followed by super class fields,
- // and so on. Don't write the klass or monitor fields of Object.class.
- const Class* sclass = clazz;
- while (!sclass->IsObjectClass()) {
- int ifieldCount = sclass->NumInstanceFields();
- for (int i = 0; i < ifieldCount; i++) {
- Field* f = sclass->GetInstanceField(i);
- size_t size;
- signatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size);
- if (size == 1) {
- rec->AddU1(f->Get32(obj));
- } else if (size == 2) {
- rec->AddU2(f->Get32(obj));
- } else if (size == 4) {
- rec->AddU4(f->Get32(obj));
- } else if (size == 8) {
- rec->AddU8(f->Get64(obj));
- } else {
- CHECK(false);
- }
- }
-
- sclass = sclass->GetSuperClass();
- }
-
- // Patch the instance field length.
- size_t savedLen = rec->length_;
- rec->length_ = sizePatchOffset;
- rec->AddU4(savedLen - (sizePatchOffset + 4));
- rec->length_ = savedLen;
- }
- }
-
- objects_in_segment_++;
- return 0;
-}
-
-} // namespace hprof
-
-} // namespace art
diff --git a/src/hprof/hprof_output.cc b/src/hprof/hprof_output.cc
deleted file mode 100644
index 3e26468..0000000
--- a/src/hprof/hprof_output.cc
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2008 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 <sys/time.h>
-#include <cutils/open_memstream.h>
-#include <time.h>
-#include <errno.h>
-#include <stdio.h>
-#include "hprof.h"
-#include "stringprintf.h"
-#include "logging.h"
-
-namespace art {
-
-namespace hprof {
-
-#define HPROF_MAGIC_STRING "JAVA PROFILE 1.0.3"
-
-#define U2_TO_BUF_BE(buf, offset, value) \
- do { \
- unsigned char *buf_ = (unsigned char *)(buf); \
- int offset_ = (int)(offset); \
- uint16_t value_ = (uint16_t)(value); \
- buf_[offset_ + 0] = (unsigned char)(value_ >> 8); \
- buf_[offset_ + 1] = (unsigned char)(value_ ); \
- } while (0)
-
-#define U4_TO_BUF_BE(buf, offset, value) \
- do { \
- unsigned char *buf_ = (unsigned char *)(buf); \
- int offset_ = (int)(offset); \
- uint32_t value_ = (uint32_t)(value); \
- buf_[offset_ + 0] = (unsigned char)(value_ >> 24); \
- buf_[offset_ + 1] = (unsigned char)(value_ >> 16); \
- buf_[offset_ + 2] = (unsigned char)(value_ >> 8); \
- buf_[offset_ + 3] = (unsigned char)(value_ ); \
- } while (0)
-
-#define U8_TO_BUF_BE(buf, offset, value) \
- do { \
- unsigned char *buf_ = (unsigned char *)(buf); \
- int offset_ = (int)(offset); \
- uint64_t value_ = (uint64_t)(value); \
- buf_[offset_ + 0] = (unsigned char)(value_ >> 56); \
- buf_[offset_ + 1] = (unsigned char)(value_ >> 48); \
- buf_[offset_ + 2] = (unsigned char)(value_ >> 40); \
- buf_[offset_ + 3] = (unsigned char)(value_ >> 32); \
- buf_[offset_ + 4] = (unsigned char)(value_ >> 24); \
- buf_[offset_ + 5] = (unsigned char)(value_ >> 16); \
- buf_[offset_ + 6] = (unsigned char)(value_ >> 8); \
- buf_[offset_ + 7] = (unsigned char)(value_ ); \
- } while (0)
-
-/*
- * Initialize an Hprof.
- */
-Hprof::Hprof(const char *outputFileName, int fd, bool writeHeader, bool directToDdms)
- : 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_(NULL),
- file_data_ptr_(NULL),
- file_data_size_(0),
- mem_fp_(NULL),
- fd_(0),
- classes_lock_("hprof classes"),
- next_string_id_(0x400000),
- strings_lock_("hprof strings") {
- // 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
- LOG(ERROR) << StringPrintf("hprof: open_memstream failed: %s", strerror(errno));
- CHECK(false);
- }
-
- direct_to_ddms_ = directToDdms;
- file_name_ = strdup(outputFileName);
- mem_fp_ = fp;
- fd_ = fd;
-
- current_record_.alloc_length_ = 128;
- current_record_.body_ = (unsigned char *)malloc(current_record_.alloc_length_);
- // TODO check for/return an error
-
- if (writeHeader) {
- char magic[] = HPROF_MAGIC_STRING;
- unsigned char buf[4];
-
- // Write the file header.
- // U1: NUL-terminated magic string.
- fwrite(magic, 1, sizeof(magic), 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);
-
- // The current time, in milliseconds since 0:00 GMT, 1/1/70.
- struct timeval now;
- uint64_t nowMs;
- if (gettimeofday(&now, NULL) < 0) {
- nowMs = 0;
- } else {
- nowMs = (uint64_t)now.tv_sec * 1000 + now.tv_usec / 1000;
- }
-
- // 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);
-
- // 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 HprofRecord::Flush(FILE *fp) {
- if (dirty_) {
- unsigned char headBuf[sizeof (uint8_t) + 2 * sizeof (uint32_t)];
-
- headBuf[0] = tag_;
- U4_TO_BUF_BE(headBuf, 1, time_);
- U4_TO_BUF_BE(headBuf, 5, length_);
-
- int nb = fwrite(headBuf, 1, sizeof(headBuf), fp);
- if (nb != sizeof(headBuf)) {
- return UNIQUE_ERROR();
- }
- nb = fwrite(body_, 1, length_, fp);
- if (nb != (int)length_) {
- return UNIQUE_ERROR();
- }
-
- dirty_ = false;
- }
- // TODO if we used less than half (or whatever) of allocLen, shrink the buffer.
- return 0;
-}
-
-int Hprof::FlushCurrentRecord() {
- return current_record_.Flush(mem_fp_);
-}
-
-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();
- }
-
- rec->dirty_ = true;
- rec->tag_ = tag;
- rec->time_ = time;
- rec->length_ = 0;
- return 0;
-}
-
-static inline int guaranteeRecordAppend(HprofRecord *rec, size_t nmore) {
- size_t minSize = rec->length_ + nmore;
- if (minSize > rec->alloc_length_) {
- size_t newAllocLen = rec->alloc_length_ * 2;
- if (newAllocLen < minSize) {
- newAllocLen = rec->alloc_length_ + nmore + nmore/2;
- }
- unsigned char *newBody = (unsigned char *)realloc(rec->body_, newAllocLen);
- if (newBody != NULL) {
- rec->body_ = newBody;
- rec->alloc_length_ = newAllocLen;
- } else {
- // TODO: set an error flag so future ops will fail
- return UNIQUE_ERROR();
- }
- }
-
- CHECK_LE(rec->length_ + nmore, rec->alloc_length_);
- return 0;
-}
-
-int HprofRecord::AddU1List(const uint8_t *values, size_t numValues) {
- int err = guaranteeRecordAppend(this, numValues);
- if (err != 0) {
- return err;
- }
-
- memcpy(body_ + length_, values, numValues);
- length_ += numValues;
-
- return 0;
-}
-
-int HprofRecord::AddU1(uint8_t value) {
- int err = guaranteeRecordAppend(this, 1);
- if (err != 0) {
- return err;
- }
-
- body_[length_++] = value;
- return 0;
-}
-
-int HprofRecord::AddUtf8String(const char *str) {
- // The terminating NUL character is NOT written.
- return AddU1List((const uint8_t *)str, strlen(str));
-}
-
-int HprofRecord::AddU2List(const uint16_t *values, size_t numValues) {
- int err = guaranteeRecordAppend(this, numValues * 2);
- if (err != 0) {
- return err;
- }
-
- unsigned char *insert = body_ + length_;
- for (size_t i = 0; i < numValues; i++) {
- U2_TO_BUF_BE(insert, 0, *values++);
- insert += sizeof(*values);
- }
- length_ += numValues * 2;
- return 0;
-}
-
-int HprofRecord::AddU2(uint16_t value) {
- return AddU2List(&value, 1);
-}
-
-int HprofRecord::AddIdList(const HprofObjectId *values, size_t numValues) {
- return AddU4List((const uint32_t*) values, numValues);
-}
-
-int HprofRecord::AddU4List(const uint32_t *values, size_t numValues) {
- int err = guaranteeRecordAppend(this, numValues * 4);
- if (err != 0) {
- return err;
- }
-
- unsigned char *insert = body_ + length_;
- for (size_t i = 0; i < numValues; i++) {
- U4_TO_BUF_BE(insert, 0, *values++);
- insert += sizeof(*values);
- }
- length_ += numValues * 4;
- return 0;
-}
-
-int HprofRecord::AddU4(uint32_t value) {
- return AddU4List(&value, 1);
-}
-
-int HprofRecord::AddId(HprofObjectId value) {
- return AddU4((uint32_t) value);
-}
-
-int HprofRecord::AddU8List(const uint64_t *values, size_t numValues) {
- int err = guaranteeRecordAppend(this, numValues * 8);
- if (err != 0) {
- return err;
- }
-
- unsigned char *insert = body_ + length_;
- for (size_t i = 0; i < numValues; i++) {
- U8_TO_BUF_BE(insert, 0, *values++);
- insert += sizeof(*values);
- }
- length_ += numValues * 8;
- return 0;
-}
-
-int HprofRecord::AddU8(uint64_t value) {
- return AddU8List(&value, 1);
-}
-
-} // namespace hprof
-
-} // namespace art
diff --git a/src/hprof/hprof_record.cc b/src/hprof/hprof_record.cc
new file mode 100644
index 0000000..1813a44
--- /dev/null
+++ b/src/hprof/hprof_record.cc
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2008 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 <sys/time.h>
+#include <time.h>
+#include <errno.h>
+#include <stdio.h>
+#include "hprof.h"
+#include "stringprintf.h"
+#include "logging.h"
+
+namespace art {
+
+namespace hprof {
+
+int HprofRecord::Flush(FILE *fp) {
+ if (dirty_) {
+ unsigned char headBuf[sizeof (uint8_t) + 2 * sizeof (uint32_t)];
+
+ headBuf[0] = tag_;
+ U4_TO_BUF_BE(headBuf, 1, time_);
+ U4_TO_BUF_BE(headBuf, 5, length_);
+
+ int nb = fwrite(headBuf, 1, sizeof(headBuf), fp);
+ if (nb != sizeof(headBuf)) {
+ return UNIQUE_ERROR();
+ }
+ nb = fwrite(body_, 1, length_, fp);
+ if (nb != (int)length_) {
+ return UNIQUE_ERROR();
+ }
+
+ dirty_ = false;
+ }
+ // TODO if we used less than half (or whatever) of allocLen, shrink the buffer.
+ return 0;
+}
+
+int HprofRecord::GuaranteeRecordAppend(size_t nmore) {
+ size_t minSize = length_ + nmore;
+ if (minSize > alloc_length_) {
+ size_t newAllocLen = alloc_length_ * 2;
+ if (newAllocLen < minSize) {
+ newAllocLen = alloc_length_ + nmore + nmore/2;
+ }
+ unsigned char *newBody = (unsigned char *)realloc(body_, newAllocLen);
+ if (newBody != NULL) {
+ body_ = newBody;
+ alloc_length_ = newAllocLen;
+ } else {
+ // TODO: set an error flag so future ops will fail
+ return UNIQUE_ERROR();
+ }
+ }
+
+ CHECK_LE(length_ + nmore, alloc_length_);
+ return 0;
+}
+
+int HprofRecord::AddU1List(const uint8_t *values, size_t numValues) {
+ int err = GuaranteeRecordAppend(numValues);
+ if (err != 0) {
+ return err;
+ }
+
+ memcpy(body_ + length_, values, numValues);
+ length_ += numValues;
+ return 0;
+}
+
+int HprofRecord::AddU1(uint8_t value) {
+ int err = GuaranteeRecordAppend(1);
+ if (err != 0) {
+ return err;
+ }
+
+ body_[length_++] = value;
+ return 0;
+}
+
+int HprofRecord::AddUtf8String(const char *str) {
+ // The terminating NUL character is NOT written.
+ return AddU1List((const uint8_t *)str, strlen(str));
+}
+
+int HprofRecord::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++) {
+ U2_TO_BUF_BE(insert, 0, *values++);
+ insert += sizeof(*values);
+ }
+ length_ += numValues * 2;
+ return 0;
+}
+
+int HprofRecord::AddU2(uint16_t value) {
+ return AddU2List(&value, 1);
+}
+
+int HprofRecord::AddIdList(const HprofObjectId *values, size_t numValues) {
+ return AddU4List((const uint32_t*) values, numValues);
+}
+
+int HprofRecord::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++) {
+ U4_TO_BUF_BE(insert, 0, *values++);
+ insert += sizeof(*values);
+ }
+ length_ += numValues * 4;
+ return 0;
+}
+
+int HprofRecord::AddU4(uint32_t value) {
+ return AddU4List(&value, 1);
+}
+
+int HprofRecord::AddId(HprofObjectId value) {
+ return AddU4((uint32_t) value);
+}
+
+int HprofRecord::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++) {
+ U8_TO_BUF_BE(insert, 0, *values++);
+ insert += sizeof(*values);
+ }
+ length_ += numValues * 8;
+ return 0;
+}
+
+int HprofRecord::AddU8(uint64_t value) {
+ return AddU8List(&value, 1);
+}
+
+} // namespace hprof
+
+} // namespace art
diff --git a/src/hprof/hprof_string.cc b/src/hprof/hprof_string.cc
deleted file mode 100644
index 96acaf3..0000000
--- a/src/hprof/hprof_string.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-/*
- * Common string pool for the profiler
- */
-#include "hprof.h"
-#include "object.h"
-#include "unordered_map.h"
-#include "logging.h"
-
-namespace art {
-
-namespace hprof {
-
-HprofStringId Hprof::LookupStringId(String* string) {
- return LookupStringId(string->ToModifiedUtf8());
-}
-
-HprofStringId Hprof::LookupStringId(const char* string) {
- return LookupStringId(std::string(string));
-}
-
-HprofStringId Hprof::LookupStringId(std::string string) {
- MutexLock mu(strings_lock_);
- if (strings_.find(string) == strings_.end()) {
- strings_[string] = next_string_id_++;
- }
- return strings_[string];
-}
-
-int Hprof::DumpStrings() {
- MutexLock mu(strings_lock_);
-
- 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;
-}
-
-} // namespace hprof
-
-} // namespace art