From: Mathieu Chartier Date: Fri, 1 Apr 2016 20:56:41 +0000 (-0700) Subject: Dump different fields in imgdiag X-Git-Tag: android-x86-7.1-r1~248^2~5^2^2~63^2^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=cb044bc5732d3ac44d22ebab01502fa303731aeb;p=android-x86%2Fart.git Dump different fields in imgdiag Dump which fields are different, also print field values. (cherry picked from commit ec1533880f3606546baceafe2db513d1a63c20da) Change-Id: Ie91ea07fb8ff7822fb7a03227cd7139e5e54db0e --- diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc index 93a0974fb..5c0eb3f11 100644 --- a/imgdiag/imgdiag.cc +++ b/imgdiag/imgdiag.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include "art_method-inl.h" #include "base/unix_file/fd_file.h" @@ -48,9 +49,9 @@ namespace art { class ImgDiagDumper { public: explicit ImgDiagDumper(std::ostream* os, - const ImageHeader& image_header, - const std::string& image_location, - pid_t image_diff_pid) + const ImageHeader& image_header, + const std::string& image_location, + pid_t image_diff_pid) : os_(os), image_header_(image_header), image_location_(image_location), @@ -58,6 +59,8 @@ class ImgDiagDumper { bool Dump() SHARED_REQUIRES(Locks::mutator_lock_) { std::ostream& os = *os_; + os << "IMAGE LOCATION: " << image_location_ << "\n\n"; + os << "MAGIC: " << image_header_.GetMagic() << "\n\n"; os << "IMAGE BEGIN: " << reinterpret_cast(image_header_.GetImageBegin()) << "\n\n"; @@ -138,7 +141,56 @@ class ImgDiagDumper { return DumpImageDiffMap(image_diff_pid, boot_map); } - // Look at /proc/$pid/mem and only diff the things from there + static std::string PrettyFieldValue(ArtField* field, mirror::Object* obj) + SHARED_REQUIRES(Locks::mutator_lock_) { + std::ostringstream oss; + switch (field->GetTypeAsPrimitiveType()) { + case Primitive::kPrimNot: { + oss << obj->GetFieldObject( + field->GetOffset()); + break; + } + case Primitive::kPrimBoolean: { + oss << static_cast(obj->GetFieldBoolean(field->GetOffset())); + break; + } + case Primitive::kPrimByte: { + oss << static_cast(obj->GetFieldByte(field->GetOffset())); + break; + } + case Primitive::kPrimChar: { + oss << obj->GetFieldChar(field->GetOffset()); + break; + } + case Primitive::kPrimShort: { + oss << obj->GetFieldShort(field->GetOffset()); + break; + } + case Primitive::kPrimInt: { + oss << obj->GetField32(field->GetOffset()); + break; + } + case Primitive::kPrimLong: { + oss << obj->GetField64(field->GetOffset()); + break; + } + case Primitive::kPrimFloat: { + oss << obj->GetField32(field->GetOffset()); + break; + } + case Primitive::kPrimDouble: { + oss << obj->GetField64(field->GetOffset()); + break; + } + case Primitive::kPrimVoid: { + oss << "void"; + break; + } + } + return oss.str(); + } + + // Look at /proc/$pid/mem and only diff the things from there bool DumpImageDiffMap(pid_t image_diff_pid, const backtrace_map_t& boot_map) SHARED_REQUIRES(Locks::mutator_lock_) { std::ostream& os = *os_; @@ -353,119 +405,119 @@ class ImgDiagDumper { // Look up local classes by their descriptor std::map local_class_map; + std::unordered_set dirty_objects; + size_t dirty_object_bytes = 0; - { - const uint8_t* begin_image_ptr = image_begin_unaligned; - const uint8_t* end_image_ptr = image_mirror_end_unaligned; - - const uint8_t* current = begin_image_ptr + RoundUp(sizeof(ImageHeader), kObjectAlignment); - while (reinterpret_cast(current) - < reinterpret_cast(end_image_ptr)) { - CHECK_ALIGNED(current, kObjectAlignment); - mirror::Object* obj = reinterpret_cast(const_cast(current)); - - // Sanity check that we are reading a real object - CHECK(obj->GetClass() != nullptr) << "Image object at address " << obj << " has null class"; - if (kUseBakerOrBrooksReadBarrier) { - obj->AssertReadBarrierPointer(); - } + const uint8_t* begin_image_ptr = image_begin_unaligned; + const uint8_t* end_image_ptr = image_mirror_end_unaligned; + + const uint8_t* current = begin_image_ptr + RoundUp(sizeof(ImageHeader), kObjectAlignment); + while (reinterpret_cast(current) < reinterpret_cast(end_image_ptr)) { + CHECK_ALIGNED(current, kObjectAlignment); + mirror::Object* obj = reinterpret_cast(const_cast(current)); + + // Sanity check that we are reading a real object + CHECK(obj->GetClass() != nullptr) << "Image object at address " << obj << " has null class"; + if (kUseBakerOrBrooksReadBarrier) { + obj->AssertReadBarrierPointer(); + } - // Iterate every page this object belongs to - bool on_dirty_page = false; - size_t page_off = 0; - size_t current_page_idx; - uintptr_t object_address; - do { - object_address = reinterpret_cast(current); - current_page_idx = object_address / kPageSize + page_off; - - if (dirty_page_set_local.find(current_page_idx) != dirty_page_set_local.end()) { - // This object is on a dirty page - on_dirty_page = true; - } + // Iterate every page this object belongs to + bool on_dirty_page = false; + size_t page_off = 0; + size_t current_page_idx; + uintptr_t object_address; + do { + object_address = reinterpret_cast(current); + current_page_idx = object_address / kPageSize + page_off; + + if (dirty_page_set_local.find(current_page_idx) != dirty_page_set_local.end()) { + // This object is on a dirty page + on_dirty_page = true; + } - page_off++; - } while ((current_page_idx * kPageSize) < - RoundUp(object_address + obj->SizeOf(), kObjectAlignment)); + page_off++; + } while ((current_page_idx * kPageSize) < + RoundUp(object_address + obj->SizeOf(), kObjectAlignment)); - mirror::Class* klass = obj->GetClass(); + mirror::Class* klass = obj->GetClass(); - bool different_object = false; + bool different_object = false; - // Check against the other object and see if they are different - ptrdiff_t offset = current - begin_image_ptr; - const uint8_t* current_remote = &remote_contents[offset]; - mirror::Object* remote_obj = reinterpret_cast( - const_cast(current_remote)); - if (memcmp(current, current_remote, obj->SizeOf()) != 0) { - different_objects++; - dirty_object_bytes += obj->SizeOf(); + // Check against the other object and see if they are different + ptrdiff_t offset = current - begin_image_ptr; + const uint8_t* current_remote = &remote_contents[offset]; + mirror::Object* remote_obj = reinterpret_cast( + const_cast(current_remote)); + if (memcmp(current, current_remote, obj->SizeOf()) != 0) { + different_objects++; + dirty_object_bytes += obj->SizeOf(); + dirty_objects.insert(obj); - ++dirty_object_class_map[klass]; + ++dirty_object_class_map[klass]; - // Go byte-by-byte and figure out what exactly got dirtied - size_t dirty_byte_count_per_object = 0; - for (size_t i = 0; i < obj->SizeOf(); ++i) { - if (current[i] != current_remote[i]) { - dirty_byte_count_per_object++; - } + // Go byte-by-byte and figure out what exactly got dirtied + size_t dirty_byte_count_per_object = 0; + for (size_t i = 0; i < obj->SizeOf(); ++i) { + if (current[i] != current_remote[i]) { + dirty_byte_count_per_object++; } - dirty_object_byte_count[klass] += dirty_byte_count_per_object; - dirty_object_size_in_bytes[klass] += obj->SizeOf(); + } + dirty_object_byte_count[klass] += dirty_byte_count_per_object; + dirty_object_size_in_bytes[klass] += obj->SizeOf(); - different_object = true; + different_object = true; - dirty_objects_by_class[klass].push_back(remote_obj); - } else { - ++clean_object_class_map[klass]; - } + dirty_objects_by_class[klass].push_back(remote_obj); + } else { + ++clean_object_class_map[klass]; + } - std::string descriptor = GetClassDescriptor(klass); - if (different_object) { - if (strcmp(descriptor.c_str(), "Ljava/lang/Class;") == 0) { - // this is a "Class" - mirror::Class* obj_as_class = reinterpret_cast(remote_obj); + std::string descriptor = GetClassDescriptor(klass); + if (different_object) { + if (klass->IsClassClass()) { + // this is a "Class" + mirror::Class* obj_as_class = reinterpret_cast(remote_obj); - // print the fields that are dirty - for (size_t i = 0; i < obj->SizeOf(); ++i) { - if (current[i] != current_remote[i]) { - class_field_dirty_count[i]++; - } + // print the fields that are dirty + for (size_t i = 0; i < obj->SizeOf(); ++i) { + if (current[i] != current_remote[i]) { + class_field_dirty_count[i]++; } + } - class_dirty_objects.push_back(obj_as_class); - } else if (strcmp(descriptor.c_str(), "Ljava/lang/reflect/ArtMethod;") == 0) { - // this is an ArtMethod - ArtMethod* art_method = reinterpret_cast(remote_obj); + class_dirty_objects.push_back(obj_as_class); + } else if (strcmp(descriptor.c_str(), "Ljava/lang/reflect/ArtMethod;") == 0) { + // this is an ArtMethod + ArtMethod* art_method = reinterpret_cast(remote_obj); - // print the fields that are dirty - for (size_t i = 0; i < obj->SizeOf(); ++i) { - if (current[i] != current_remote[i]) { - art_method_field_dirty_count[i]++; - } + // print the fields that are dirty + for (size_t i = 0; i < obj->SizeOf(); ++i) { + if (current[i] != current_remote[i]) { + art_method_field_dirty_count[i]++; } - - art_method_dirty_objects.push_back(art_method); } - } else if (on_dirty_page) { - // This object was either never mutated or got mutated back to the same value. - // TODO: Do I want to distinguish a "different" vs a "dirty" page here? - false_dirty_objects.push_back(obj); - false_dirty_objects_map[klass].push_back(obj); - false_dirty_object_bytes += obj->SizeOf(); - false_dirty_byte_count[obj->GetClass()] += obj->SizeOf(); - false_dirty_object_count[obj->GetClass()] += 1; - } - if (strcmp(descriptor.c_str(), "Ljava/lang/Class;") == 0) { - local_class_map[descriptor] = reinterpret_cast(obj); - remote_class_map[descriptor] = reinterpret_cast(remote_obj); + art_method_dirty_objects.push_back(art_method); } + } else if (on_dirty_page) { + // This object was either never mutated or got mutated back to the same value. + // TODO: Do I want to distinguish a "different" vs a "dirty" page here? + false_dirty_objects.push_back(obj); + false_dirty_objects_map[klass].push_back(obj); + false_dirty_object_bytes += obj->SizeOf(); + false_dirty_byte_count[obj->GetClass()] += obj->SizeOf(); + false_dirty_object_count[obj->GetClass()] += 1; + } - // Unconditionally store the class descriptor in case we need it later - class_to_descriptor_map[klass] = descriptor; - current += RoundUp(obj->SizeOf(), kObjectAlignment); + if (strcmp(descriptor.c_str(), "Ljava/lang/Class;") == 0) { + local_class_map[descriptor] = reinterpret_cast(obj); + remote_class_map[descriptor] = reinterpret_cast(remote_obj); } + + // Unconditionally store the class descriptor in case we need it later + class_to_descriptor_map[klass] = descriptor; + current += RoundUp(obj->SizeOf(), kObjectAlignment); } // Looking at only dirty pages, figure out how many of those bytes belong to dirty objects. @@ -492,6 +544,71 @@ class ImgDiagDumper { auto dirty_object_class_values = SortByValueDesc(dirty_object_class_map); auto clean_object_class_values = SortByValueDesc(clean_object_class_map); + os << "\n" << " Dirty objects: " << dirty_objects.size() << "\n"; + for (mirror::Object* obj : dirty_objects) { + const char* tabs = " "; + // Attempt to find fields for all dirty bytes. + mirror::Class* klass = obj->GetClass(); + if (obj->IsClass()) { + os << tabs << "Class " << PrettyClass(obj->AsClass()) << " " << obj << "\n"; + } else { + os << tabs << "Instance of " << PrettyClass(klass) << " " << obj << "\n"; + } + + std::unordered_set dirty_instance_fields; + std::unordered_set dirty_static_fields; + const uint8_t* obj_bytes = reinterpret_cast(obj); + ptrdiff_t offset = obj_bytes - begin_image_ptr; + uint8_t* remote_bytes = &remote_contents[offset]; + mirror::Object* remote_obj = reinterpret_cast(remote_bytes); + for (size_t i = 0, count = obj->SizeOf(); i < count; ++i) { + if (obj_bytes[i] != remote_bytes[i]) { + ArtField* field = ArtField::FindInstanceFieldWithOffset(klass, i); + if (field != nullptr) { + dirty_instance_fields.insert(field); + } else if (obj->IsClass()) { + field = ArtField::FindStaticFieldWithOffset(obj->AsClass(), i); + if (field != nullptr) { + dirty_static_fields.insert(field); + } + } + if (field == nullptr) { + if (klass->IsArrayClass()) { + mirror::Class* component_type = klass->GetComponentType(); + Primitive::Type primitive_type = component_type->GetPrimitiveType(); + size_t component_size = Primitive::ComponentSize(primitive_type); + size_t data_offset = mirror::Array::DataOffset(component_size).Uint32Value(); + if (i >= data_offset) { + os << tabs << "Dirty array element " << (i - data_offset) / component_size << "\n"; + // Skip to next element to prevent spam. + i += component_size - 1; + continue; + } + } + os << tabs << "No field for byte offset " << i << "\n"; + } + } + } + // Dump different fields. TODO: Dump field contents. + if (!dirty_instance_fields.empty()) { + os << tabs << "Dirty instance fields " << dirty_instance_fields.size() << "\n"; + for (ArtField* field : dirty_instance_fields) { + os << tabs << PrettyField(field) + << " original=" << PrettyFieldValue(field, obj) + << " remote=" << PrettyFieldValue(field, remote_obj) << "\n"; + } + } + if (!dirty_static_fields.empty()) { + os << tabs << "Dirty static fields " << dirty_static_fields.size() << "\n"; + for (ArtField* field : dirty_static_fields) { + os << tabs << PrettyField(field) + << " original=" << PrettyFieldValue(field, obj) + << " remote=" << PrettyFieldValue(field, remote_obj) << "\n"; + } + } + os << "\n"; + } + os << "\n" << " Dirty object count by class:\n"; for (const auto& vk_pair : dirty_object_class_values) { int dirty_object_count = vk_pair.first; diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h index 3463b0d84..d91149701 100644 --- a/runtime/art_field-inl.h +++ b/runtime/art_field-inl.h @@ -343,6 +343,49 @@ inline void ArtField::UpdateObjects(const Visitor& visitor) { } } +// If kExactOffset is true then we only find the matching offset, not the field containing the +// offset. +template +static inline ArtField* FindFieldWithOffset( + const IterationRange>& fields, + uint32_t field_offset) SHARED_REQUIRES(Locks::mutator_lock_) { + for (ArtField& field : fields) { + if (kExactOffset) { + if (field.GetOffset().Uint32Value() == field_offset) { + return &field; + } + } else { + const uint32_t offset = field.GetOffset().Uint32Value(); + Primitive::Type type = field.GetTypeAsPrimitiveType(); + const size_t field_size = Primitive::ComponentSize(type); + DCHECK_GT(field_size, 0u); + if (offset <= field_offset && field_offset < offset + field_size) { + return &field; + } + } + } + return nullptr; +} + +template +inline ArtField* ArtField::FindInstanceFieldWithOffset(mirror::Class* klass, + uint32_t field_offset) { + DCHECK(klass != nullptr); + ArtField* field = FindFieldWithOffset(klass->GetIFields(), field_offset); + if (field != nullptr) { + return field; + } + // We did not find field in the class: look into superclass. + return (klass->GetSuperClass() != nullptr) ? + FindInstanceFieldWithOffset(klass->GetSuperClass(), field_offset) : nullptr; +} + +template +inline ArtField* ArtField::FindStaticFieldWithOffset(mirror::Class* klass, uint32_t field_offset) { + DCHECK(klass != nullptr); + return FindFieldWithOffset(klass->GetSFields(), field_offset); +} + } // namespace art #endif // ART_RUNTIME_ART_FIELD_INL_H_ diff --git a/runtime/art_field.cc b/runtime/art_field.cc index 3737e0dde..ea5078e9b 100644 --- a/runtime/art_field.cc +++ b/runtime/art_field.cc @@ -47,28 +47,6 @@ void ArtField::SetOffset(MemberOffset num_bytes) { offset_ = num_bytes.Uint32Value(); } -ArtField* ArtField::FindInstanceFieldWithOffset(mirror::Class* klass, uint32_t field_offset) { - DCHECK(klass != nullptr); - for (ArtField& field : klass->GetIFields()) { - if (field.GetOffset().Uint32Value() == field_offset) { - return &field; - } - } - // We did not find field in the class: look into superclass. - return (klass->GetSuperClass() != nullptr) ? - FindInstanceFieldWithOffset(klass->GetSuperClass(), field_offset) : nullptr; -} - -ArtField* ArtField::FindStaticFieldWithOffset(mirror::Class* klass, uint32_t field_offset) { - DCHECK(klass != nullptr); - for (ArtField& field : klass->GetSFields()) { - if (field.GetOffset().Uint32Value() == field_offset) { - return &field; - } - } - return nullptr; -} - mirror::Class* ArtField::ProxyFindSystemClass(const char* descriptor) { DCHECK(GetDeclaringClass()->IsProxyClass()); return Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(), descriptor); diff --git a/runtime/art_field.h b/runtime/art_field.h index ee1ba1fb5..b64b70fa8 100644 --- a/runtime/art_field.h +++ b/runtime/art_field.h @@ -159,9 +159,16 @@ class ArtField FINAL { } // Returns an instance field with this offset in the given class or null if not found. + // If kExactOffset is true then we only find the matching offset, not the field containing the + // offset. + template static ArtField* FindInstanceFieldWithOffset(mirror::Class* klass, uint32_t field_offset) SHARED_REQUIRES(Locks::mutator_lock_); + // Returns a static field with this offset in the given class or null if not found. + // If kExactOffset is true then we only find the matching offset, not the field containing the + // offset. + template static ArtField* FindStaticFieldWithOffset(mirror::Class* klass, uint32_t field_offset) SHARED_REQUIRES(Locks::mutator_lock_);