Oatdump fields of objects using reflection

Previously part of Change Ieae1b74c

Change-Id: I7290cc97c3d7eb22f2bbf7964bc9a77d7036d70f
diff --git a/src/oatdump.cc b/src/oatdump.cc
index 1b54b24..5b9d271 100644
--- a/src/oatdump.cc
+++ b/src/oatdump.cc
@@ -239,7 +239,15 @@
             = down_cast<ObjectArray<Object>*>(image_root_object);
         //  = image_root_object->AsObjectArray<Object>();
         for (int i = 0; i < image_root_object_array->GetLength(); i++) {
-            os << StringPrintf("\t%d: %p\n", i, image_root_object_array->Get(i));
+          Object* value = image_root_object_array->Get(i);
+          if (value != NULL) {
+            os << "\t" << i << ": ";
+            std::string summary;
+            PrettyObjectValue(summary, value->GetClass(), value);
+            os << summary;
+          } else {
+            os << StringPrintf("\t%d: null\n", i);
+          }
         }
       }
     }
@@ -292,6 +300,59 @@
 
   ~ImageDump() {}
 
+  static void PrettyObjectValue(std::string& summary, Class* type, Object* value) {
+    CHECK(type != NULL);
+    if (value == NULL) {
+      StringAppendF(&summary, "null   %s\n", PrettyDescriptor(type).c_str());
+    } else if (type->IsStringClass()) {
+      String* string = value->AsString();
+      StringAppendF(&summary, "%p   String: \"%s\"\n", string, string->ToModifiedUtf8().c_str());
+    } else if (value->IsClass()) {
+      Class* klass = value->AsClass();
+      StringAppendF(&summary, "%p   Class: %s\n", klass, PrettyDescriptor(klass).c_str());
+    } else if (value->IsField()) {
+      Field* field = value->AsField();
+      StringAppendF(&summary, "%p   Field: %s\n", field, PrettyField(field).c_str());
+    } else if (value->IsMethod()) {
+      Method* method = value->AsMethod();
+      StringAppendF(&summary, "%p   Method: %s\n", method, PrettyMethod(method).c_str());
+    } else {
+      StringAppendF(&summary, "%p   %s\n", value, PrettyDescriptor(type).c_str());
+    }
+  }
+
+  static void PrintField(std::string& summary, Field* field, Object* obj) {
+    FieldHelper fh(field);
+    Class* type = fh.GetType();
+    StringAppendF(&summary, "\t%s: ", fh.GetName());
+    if (type->IsPrimitiveLong()) {
+      StringAppendF(&summary, "%lld (0x%llx)\n", field->Get64(obj), field->Get64(obj));
+    } else if (type->IsPrimitiveDouble()) {
+      StringAppendF(&summary, "%f (%a)\n", field->GetDouble(obj), field->GetDouble(obj));
+    } else if (type->IsPrimitiveFloat()) {
+      StringAppendF(&summary, "%f (%a)\n", field->GetFloat(obj), field->GetFloat(obj));
+    } else if (type->IsPrimitive()){
+      StringAppendF(&summary, "%d (0x%x)\n", field->Get32(obj), field->Get32(obj));
+    } else {
+      Object* value = field->GetObj(obj);
+      PrettyObjectValue(summary, type, value);
+    }
+  }
+
+  static void DumpFields(std::string& summary, Object* obj, Class* klass) {
+    Class* super = klass->GetSuperClass();
+    if (super != NULL) {
+      DumpFields(summary, obj, super);
+    }
+    ObjectArray<Field>* fields = klass->GetIFields();
+    if (fields != NULL) {
+      for (int32_t i = 0; i < fields->GetLength(); i++) {
+        Field* field = fields->Get(i);
+        PrintField(summary, field, obj);
+      }
+    }
+  }
+
   static void Callback(Object* obj, void* arg) {
     DCHECK(obj != NULL);
     DCHECK(arg != NULL);
@@ -306,59 +367,68 @@
     state->stats_.alignment_bytes += alignment_bytes;
 
     std::string summary;
-    StringAppendF(&summary, "%p: ", obj);
-    if (obj->IsClass()) {
+    Class* obj_class = obj->GetClass();
+    if (obj_class->IsArrayClass()) {
+      StringAppendF(&summary, "%p: %s length:%d\n", obj, PrettyDescriptor(obj_class).c_str(),
+                    obj->AsArray()->GetLength());
+    } else if (obj->IsClass()) {
       Class* klass = obj->AsClass();
-      summary += "CLASS ";
-      summary += ClassHelper(klass).GetDescriptor();
+      StringAppendF(&summary, "%p: java.lang.Class \"%s\" (", obj,
+                    PrettyDescriptor(klass).c_str());
       std::ostringstream ss;
-      ss << " (" << klass->GetStatus() << ")";
+      ss << klass->GetStatus() << ")\n";
       summary += ss.str();
+    } else if (obj->IsField()) {
+      StringAppendF(&summary, "%p: java.lang.reflect.Field %s\n", obj,
+                    PrettyField(obj->AsField()).c_str());
+    } else if (obj->IsMethod()) {
+      StringAppendF(&summary, "%p: java.lang.reflect.Method %s\n", obj,
+                    PrettyMethod(obj->AsMethod()).c_str());
+    } else if (obj_class->IsStringClass()) {
+      StringAppendF(&summary, "%p: java.lang.String \"%s\"\n", obj,
+                    obj->AsString()->ToModifiedUtf8().c_str());
+    } else {
+      StringAppendF(&summary, "%p: %s\n", obj, PrettyDescriptor(obj_class).c_str());
+    }
+    DumpFields(summary, obj, obj_class);
+    if (obj->IsObjectArray()) {
+      ObjectArray<Object>* obj_array = obj->AsObjectArray<Object>();
+      int32_t length = obj_array->GetLength();
+      for (int32_t i = 0; i < length; i++) {
+        Object* value = obj_array->Get(i);
+        size_t run = 0;
+        for (int32_t j = i + 1; j < length; j++) {
+          if (value == obj_array->Get(j)) {
+            run++;
+          } else {
+            break;
+          }
+        }
+        if (run == 0) {
+          StringAppendF(&summary, "\t%d: ", i);
+        } else {
+          StringAppendF(&summary, "\t%d to %d: ", i, i + run);
+          i = i + run;
+        }
+        Class* value_class = value == NULL ? obj_class->GetComponentType() : value->GetClass();
+        PrettyObjectValue(summary, value_class, value);
+      }
+    } else if (obj->IsClass()) {
+      ObjectArray<Field>* sfields = obj->AsClass()->GetSFields();
+      if (sfields != NULL) {
+        summary += "\t\tSTATICS:\n";
+        for (int32_t i = 0; i < sfields->GetLength(); i++) {
+          Field* field = sfields->Get(i);
+          PrintField(summary, field, NULL);
+        }
+      }
     } else if (obj->IsMethod()) {
       Method* method = obj->AsMethod();
-      StringAppendF(&summary, "METHOD %s", PrettyMethod(method).c_str());
-    } else if (obj->IsField()) {
-      Field* field = obj->AsField();
-      StringAppendF(&summary, "FIELD %s", PrettyField(field).c_str());
-    } else if (obj->IsArrayInstance()) {
-      StringAppendF(&summary, "ARRAY %d", obj->AsArray()->GetLength());
-    } else if (obj->GetClass()->IsStringClass()) {
-      StringAppendF(&summary, "STRING %s", obj->AsString()->ToModifiedUtf8().c_str());
-    } else {
-      StringAppendF(&summary, "OBJECT");
-    }
-    StringAppendF(&summary, "\n");
-    std::string descriptor(ClassHelper(obj->GetClass()).GetDescriptor());
-    StringAppendF(&summary, "\tclass %p: %s\n", obj->GetClass(), descriptor.c_str());
-    state->stats_.descriptor_to_bytes[descriptor] += object_bytes;
-    state->stats_.descriptor_to_count[descriptor] += 1;
-    // StringAppendF(&summary, "\tsize %d (alignment padding %d)\n",
-    //               object_bytes, RoundUp(object_bytes, kObjectAlignment) - object_bytes);
-    if (obj->IsMethod()) {
-      Method* method = obj->AsMethod();
-      if (!method->IsCalleeSaveMethod()) {
-        const int8_t* code =reinterpret_cast<const int8_t*>(method->GetCode());
-        StringAppendF(&summary, "\tCODE     %p\n", code);
-
-        const Method::InvokeStub* invoke_stub = method->GetInvokeStub();
-        StringAppendF(&summary, "\tJNI STUB %p\n", invoke_stub);
-      }
       if (method->IsNative()) {
-        if (method->IsRegistered()) {
-          StringAppendF(&summary, "\tNATIVE REGISTERED %p\n", method->GetNativeMethod());
-        } else {
-          StringAppendF(&summary, "\tNATIVE UNREGISTERED\n");
-        }
         DCHECK(method->GetGcMap() == NULL) << PrettyMethod(method);
         DCHECK_EQ(0U, method->GetGcMapLength()) << PrettyMethod(method);
         DCHECK(method->GetMappingTable() == NULL) << PrettyMethod(method);
-      } else if (method->IsAbstract()) {
-        StringAppendF(&summary, "\tABSTRACT\n");
-        DCHECK(method->GetGcMap() == NULL) << PrettyMethod(method);
-        DCHECK_EQ(0U, method->GetGcMapLength()) << PrettyMethod(method);
-        DCHECK(method->GetMappingTable() == NULL) << PrettyMethod(method);
-      } else if (method->IsCalleeSaveMethod()) {
-        StringAppendF(&summary, "\tCALLEE SAVE METHOD\n");
+      } else if (method->IsAbstract() || method->IsCalleeSaveMethod()) {
         DCHECK(method->GetGcMap() == NULL) << PrettyMethod(method);
         DCHECK_EQ(0U, method->GetGcMapLength()) << PrettyMethod(method);
         DCHECK(method->GetMappingTable() == NULL) << PrettyMethod(method);
@@ -368,20 +438,22 @@
 
         size_t register_map_bytes = method->GetGcMapLength();
         state->stats_.register_map_bytes += register_map_bytes;
-        StringAppendF(&summary, "GC=%zd ", register_map_bytes);
 
         size_t pc_mapping_table_bytes = method->GetMappingTableLength();
         state->stats_.pc_mapping_table_bytes += pc_mapping_table_bytes;
-        StringAppendF(&summary, "Mapping=%zd ", pc_mapping_table_bytes);
 
         const DexFile::CodeItem* code_item = MethodHelper(method).GetCodeItem();
         size_t dex_instruction_bytes = code_item->insns_size_in_code_units_ * 2;
         state->stats_.dex_instruction_bytes += dex_instruction_bytes;
 
-        StringAppendF(&summary, "\tSIZE Code=%zd GC=%zd Mapping=%zd",
+        StringAppendF(&summary, "\t\tSIZE: Dex Instructions=%zd GC=%zd Mapping=%zd\n",
                       dex_instruction_bytes, register_map_bytes, pc_mapping_table_bytes);
       }
     }
+    std::string descriptor(ClassHelper(obj_class).GetDescriptor());
+    state->stats_.descriptor_to_bytes[descriptor] += object_bytes;
+    state->stats_.descriptor_to_count[descriptor] += 1;
+
     state->os_ << summary << std::flush;
   }