std::ostream& operator<<(std::ostream& os, RemoteProcesses remotes) { switch (remotes) { case RemoteProcesses::kImageOnly: os << "ImageOnly"; break; case RemoteProcesses::kZygoteOnly: os << "ZygoteOnly"; break; case RemoteProcesses::kImageAndZygote: os << "ImageAndZygote"; break;
} return os;
}
struct MappingData { // The count of pages that are considered dirty by the OS.
size_t dirty_pages = 0; // The count of pages that differ by at least one byte.
size_t different_pages = 0; // The count of differing bytes.
size_t different_bytes = 0; // The count of differing four-byte units.
size_t different_int32s = 0; // The count of pages that have mapping count == 1.
size_t private_pages = 0; // The count of private pages that are also dirty.
size_t private_dirty_pages = 0; // The count of pages that are marked dirty but do not differ.
size_t false_dirty_pages = 0; // Set of the local virtual page indices that are dirty.
std::set<size_t> dirty_page_set; // Private dirty page counts for each section of the image
std::array<size_t, ImageHeader::kSectionCount> private_dirty_pages_for_section = {};
};
static std::string PrettyFieldValue(ArtField* field, mirror::Object* object)
REQUIRES_SHARED(Locks::mutator_lock_) {
std::ostringstream oss; switch (field->GetTypeAsPrimitiveType()) { case Primitive::kPrimNot: {
oss << object->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(
field->GetOffset()); break;
} case Primitive::kPrimBoolean: {
oss << static_cast<bool>(object->GetFieldBoolean<kVerifyNone>(field->GetOffset())); break;
} case Primitive::kPrimByte: {
oss << static_cast<int32_t>(object->GetFieldByte<kVerifyNone>(field->GetOffset())); break;
} case Primitive::kPrimChar: {
oss << object->GetFieldChar<kVerifyNone>(field->GetOffset()); break;
} case Primitive::kPrimShort: {
oss << object->GetFieldShort<kVerifyNone>(field->GetOffset()); break;
} case Primitive::kPrimInt: {
oss << object->GetField32<kVerifyNone>(field->GetOffset()); break;
} case Primitive::kPrimLong: {
oss << object->GetField64<kVerifyNone>(field->GetOffset()); break;
} case Primitive::kPrimFloat: {
oss << object->GetField32<kVerifyNone>(field->GetOffset()); break;
} case Primitive::kPrimDouble: {
oss << object->GetField64<kVerifyNone>(field->GetOffset()); break;
} case Primitive::kPrimVoid: {
oss << "void"; break;
}
} return oss.str();
}
template <typename K, typename V, typename D> static std::vector<std::pair<V, K>> SortByValueDesc( const std::map<K, D> map,
std::function<V(const D&)> value_mapper = [](const D& d) { returnstatic_cast<V>(d); }) { // Store value->key so that we can use the default sort from pair which // sorts by value first and then key
std::vector<std::pair<V, K>> value_key_vector;
value_key_vector.reserve(map.size()); for (constauto& kv_pair : map) {
value_key_vector.push_back(std::make_pair(value_mapper(kv_pair.second), kv_pair.first));
}
// Fixup a remote pointer that we read from a foreign boot.art to point to our own memory. // Returned pointer will point to inside of remote_contents. template <typename T> static ObjPtr<T> FixUpRemotePointer(ObjPtr<T> remote_ptr,
ArrayRef<uint8_t> remote_contents, const android::procinfo::MapInfo& boot_map)
REQUIRES_SHARED(Locks::mutator_lock_) { if (remote_ptr == nullptr) { return nullptr;
}
// In the case the remote pointer is out of range, it probably belongs to another image. // Just return null for this case. if (remote < boot_map.start || remote >= boot_map.end) { return nullptr;
}
// entry1 and entry2 might be relocated, this means we must use the runtime image's entry // (image_entry) to avoid crashes. template <typename T> staticbool EntriesDiffer(T* image_entry,
T* entry1,
T* entry2) REQUIRES_SHARED(Locks::mutator_lock_) { // Use the image entry since entry1 and entry2 might both be remote and relocated. return memcmp(entry1, entry2, EntrySize(image_entry)) != 0;
}
// The output stream to write to.
std::ostream& os_; // The byte contents of the remote (image) process' image.
ArrayRef<uint8_t> remote_contents_; // The byte contents of the zygote process' image.
ArrayRef<uint8_t> zygote_contents_; const android::procinfo::MapInfo& boot_map_; const ImageHeader& image_header_;
// Count of entries that are different.
size_t different_entries_;
// Local entries that are dirty (differ in at least one byte).
size_t dirty_entry_bytes_;
std::vector<T*> dirty_entries_;
// Local entries that are clean, but located on dirty pages.
size_t false_dirty_entry_bytes_;
std::vector<T*> false_dirty_entries_;
// Image dirty entries // If zygote_pid_only_ == true, these are shared dirty entries in the zygote. // If zygote_pid_only_ == false, these are private dirty entries in the application.
std::set<T*> image_dirty_entries_;
std::map<off_t /* field offset */, size_t /* count */> field_dirty_count_;
template <typename T> class RegionSpecializedBase : public RegionCommon<T> {
};
// Calls VisitFunc for each non-null (reference)Object/ArtField pair. // Doesn't work with ObjectArray instances, because array elements don't have ArtField. class ReferenceFieldVisitor { public: using VisitFunc = std::function<void(mirror::Object&, ArtField&)>;
ArtField* field = nullptr; // Don't use Object::FindFieldByOffset, because it can't find instance fields in classes. // field = obj->FindFieldByOffset(offset); if (is_static) {
CHECK(obj->IsClass());
field = ArtField::FindStaticFieldWithOffset(obj->AsClass(), offset.Uint32Value());
} else {
field = ArtField::FindInstanceFieldWithOffset(obj->GetClass(), offset.Uint32Value());
}
CHECK(field != nullptr);
visit_func_(*field_obj, *field);
}
// Region analysis for mirror::Objects class ImgObjectVisitor : public ObjectVisitor { public: using ComputeDirtyFunc = std::function<void(mirror::Object* object)>; explicit ImgObjectVisitor(ComputeDirtyFunc dirty_func) : dirty_func_(std::move(dirty_func)) {}
~ImgObjectVisitor() override { }
void Visit(mirror::Object* object) override REQUIRES_SHARED(Locks::mutator_lock_) { // Check that we are reading a real mirror::Object
CHECK(object->GetClass() != nullptr) << "Image object at address "
<< object
<< " has null class"; if (kUseBakerReadBarrier) {
object->AssertReadBarrierState();
}
dirty_func_(object);
}
private: const ComputeDirtyFunc dirty_func_;
};
struct ParentInfo {
mirror::Object* parent = nullptr; // Field name and type of the parent object in the format: <field_name>:<field_type_descriptor> // Note: <field_name> can be an integer if parent is an Array object.
std::string path;
};
using ParentMap = std::unordered_map<mirror::Object*, ParentInfo>;
// Returns the "path" from root class to an object in the format: // <dex_location> <class_descriptor>(.<field_name>:<field_type_descriptor>)* // <dex_location> is either a full path to the dex file where the class is // defined or "primitive" if the class is a primitive array.
std::string GetPathFromClass(mirror::Object* obj, const ParentMap& parent_map)
REQUIRES_SHARED(Locks::mutator_lock_) { auto parent_info_it = parent_map.find(obj);
std::string path; while (parent_info_it != parent_map.end() && parent_info_it->second.parent != nullptr) { const ParentInfo& parent_info = parent_info_it->second;
path = ART_FORMAT(".{}{}", parent_info.path, path);
parent_info_it = parent_map.find(parent_info.parent);
}
if (parent_info_it == parent_map.end()) { return"<no path from class>";
}
// Prepend dex location to the path. // Use array value type if class is an array. while (klass->IsArrayClass()) {
klass = klass->GetComponentType();
}
std::string dex_location = klass->IsPrimitive() ? "primitive" : klass->GetDexFile().GetLocation();
path = ART_FORMAT("{} {}", dex_location, path);
return path;
}
// Calculate a map of: object -> parent and parent field that refers to the object. // Class objects are considered roots, they have entries in the parent_map, but their parent==null.
ParentMap CalculateParentMap(const std::vector<const ImageHeader*>& image_headers)
REQUIRES_SHARED(Locks::mutator_lock_) {
ParentMap parent_map;
std::vector<mirror::Object*> next;
const int32_t length = array->GetLength(); for (int32_t i = 0; i < length; ++i) {
ObjPtr<mirror::Object> elem = array->Get(i); if (elem != nullptr && parent_map.count(elem.Ptr()) == 0) {
std::string temp;
std::string path = ART_FORMAT("{}:{}", i, elem->GetClass()->GetDescriptor(&temp));
parent_map[elem.Ptr()] = ParentInfo{parent_obj, path};
next.push_back(elem.Ptr());
}
}
};
// Use DFS to traverse all objects that are reachable from classes. while (!next.empty()) {
mirror::Object* parent_obj = next.back();
next.pop_back();
// Array elements don't have ArtField, handle them separately. if (parent_obj->IsObjectArray()) {
process_array_elements(parent_obj);
} else {
process_object_fields(parent_obj);
}
}
return parent_map;
}
// Count non-string objects that are not reachable from classes. // Strings are skipped because they are considered clean in dex2oat and not used for dirty // object layout optimization.
size_t CountUnreachableObjects(const std::unordered_map<mirror::Object*, ParentInfo>& parent_map, const std::vector<const ImageHeader*>& image_headers)
REQUIRES_SHARED(Locks::mutator_lock_) {
size_t non_reachable = 0;
ImgObjectVisitor count_non_reachable_visitor(
[&](mirror::Object* entry) REQUIRES_SHARED(Locks::mutator_lock_) { if (parent_map.count(entry) == 0 && !entry->IsString()) {
non_reachable += 1;
}
}); for (const ImageHeader* image_header : image_headers) {
uint8_t* image_begin = image_header->GetImageBegin();
PointerSize pointer_size = image_header->GetPointerSize();
image_header->VisitObjects(&count_non_reachable_visitor, image_begin, pointer_size);
}
void VisitEntry(mirror::Object* entry)
REQUIRES_SHARED(Locks::mutator_lock_) { // Unconditionally store the class descriptor in case we need it later
mirror::Class* klass = entry->GetClass();
class_data_[klass].descriptor = GetClassDescriptor(klass);
}
// Walk over the type T entries in theregion between begin_image_ptr and end_image_ptr, // collecting and reporting data regarding dirty, difference, etc. void ProcessRegion(const MappingData& mapping_data,
RemoteProcesses remotes, const uint8_t* begin_image_ptr)
REQUIRES_SHARED(Locks::mutator_lock_) { typename RegionSpecializedBase<T>::VisitorClass visitor(
[this, begin_image_ptr, &mapping_data](T* entry) REQUIRES_SHARED(Locks::mutator_lock_) { this->ComputeEntryDirty(entry, begin_image_ptr, mapping_data.dirty_page_set);
});
PointerSize pointer_size = InstructionSetPointerSize(Runtime::Current()->GetInstructionSet());
RegionSpecializedBase<T>::VisitEntries(&visitor, const_cast<uint8_t*>(begin_image_ptr),
pointer_size);
// Looking at only dirty pages, figure out how many of those bytes belong to dirty entries. // TODO: fix this now that there are multiple regions in a mapping. float true_dirtied_percent =
(RegionCommon<T>::GetDirtyEntryBytes() * 1.0f) /
(mapping_data.dirty_pages * MemMap::GetPageSize());
// Entry specific statistics.
os_ << RegionCommon<T>::GetDifferentEntryCount() << " different entries, \n "
<< RegionCommon<T>::GetDirtyEntryBytes() << " different entry [bytes], \n "
<< RegionCommon<T>::GetFalseDirtyEntryCount() << " false dirty entries,\n "
<< RegionCommon<T>::GetFalseDirtyEntryBytes() << " false dirty entry [bytes], \n "
<< true_dirtied_percent << " different entries-vs-total in a dirty page;\n "
<< "\n";
const uint8_t* base_ptr = begin_image_ptr; switch (remotes) { case RemoteProcesses::kZygoteOnly:
os_ << " Zygote shared dirty entries: "; break; case RemoteProcesses::kImageAndZygote:
os_ << " Application dirty entries (private dirty): "; // If we are dumping private dirty, diff against the zygote map to make it clearer what // fields caused the page to be private dirty.
base_ptr = RegionCommon<T>::zygote_contents_.data(); break; case RemoteProcesses::kImageOnly:
os_ << " Application dirty entries (unknown whether private or shared dirty): "; break;
}
DiffDirtyEntries(RegionCommon<T>::image_dirty_entries_,
begin_image_ptr,
RegionCommon<T>::remote_contents_,
base_ptr, /*log_dirty_objects=*/true);
RegionSpecializedBase<T>::DumpDirtyObjects();
RegionSpecializedBase<T>::DumpDirtyEntries();
RegionSpecializedBase<T>::DumpFalseDirtyEntries();
RegionSpecializedBase<T>::DumpCleanEntries();
}
void ComputeEntryDirty(T* entry, const uint8_t* begin_image_ptr, const std::set<size_t>& dirty_pages)
REQUIRES_SHARED(Locks::mutator_lock_) { // Set up pointers in the remote and the zygote for comparison.
uint8_t* current = reinterpret_cast<uint8_t*>(entry);
ptrdiff_t offset = current - begin_image_ptr;
T* entry_remote = reinterpret_cast<T*>(const_cast<uint8_t*>(&RegionCommon<T>::remote_contents_[offset])); constbool have_zygote = !RegionCommon<T>::zygote_contents_.empty(); const uint8_t* current_zygote =
have_zygote ? &RegionCommon<T>::zygote_contents_[offset] : nullptr;
T* entry_zygote = reinterpret_cast<T*>(const_cast<uint8_t*>(current_zygote)); // Visit and classify entries at the current location.
RegionSpecializedBase<T>::VisitEntry(entry);
// Test private dirty first. bool is_dirty = false; if (have_zygote) { if (EntriesDiffer(entry, entry_zygote, entry_remote)) { // Private dirty, app vs zygote.
is_dirty = true;
RegionCommon<T>::AddImageDirtyEntry(entry);
}
} elseif (EntriesDiffer(entry, entry_remote, entry)) { // Shared or private dirty, app vs image.
is_dirty = true;
RegionCommon<T>::AddImageDirtyEntry(entry);
} if (is_dirty) { // TODO: Add support dirty entries in zygote and image.
RegionSpecializedBase<T>::AddDirtyEntry(entry, entry_remote);
} else {
RegionSpecializedBase<T>::AddCleanEntry(entry); if (RegionCommon<T>::IsEntryOnDirtyPage(entry, dirty_pages)) { // This entry 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?
RegionSpecializedBase<T>::AddFalseDirtyEntry(entry);
}
}
}
if (image_diff_pid_ < 0 || zygote_diff_pid_ < 0) { // TODO: ComputeDirtyBytes must be modified // to support single app/zygote to bootimage comparison
os << "Both --image-diff-pid and --zygote-diff-pid must be specified.\n"; returnfalse;
}
// To avoid the combinations of command-line argument use cases: // If the user invoked with only --zygote-diff-pid, shuffle that to // image_diff_pid_, invalidate zygote_diff_pid_, and remember that // image_diff_pid_ is now special. if (image_diff_pid_ < 0) {
image_diff_pid_ = zygote_diff_pid_;
zygote_diff_pid_ = -1;
zygote_pid_only_ = true;
}
{ struct stat sts;
std::string proc_pid_str =
StringPrintf("/proc/%ld", static_cast<long>(image_diff_pid_)); // NOLINT [runtime/int] if (stat(proc_pid_str.c_str(), &sts) == -1) {
os << "Process does not exist"; returnfalse;
}
}
auto open_proc_maps = [&os](pid_t pid, /*out*/ std::vector<android::procinfo::MapInfo>* proc_maps) { if (!android::procinfo::ReadProcessMaps(pid, proc_maps)) {
os << "Could not read process maps for " << pid; returnfalse;
} returntrue;
}; auto open_file = [&os] (constchar* file_name, /*out*/ std::unique_ptr<File>* file) {
file->reset(OS::OpenFileForReading(file_name)); if (*file == nullptr) {
os << "Failed to open " << file_name << " for reading"; returnfalse;
} returntrue;
}; auto open_mem_file = [&open_file](pid_t pid, /*out*/ std::unique_ptr<File>* mem_file) { // Open /proc/<pid>/mem and for reading remote contents.
std::string mem_file_name =
StringPrintf("/proc/%ld/mem", static_cast<long>(pid)); // NOLINT [runtime/int] return open_file(mem_file_name.c_str(), mem_file);
}; auto open_pagemap_file = [&open_file](pid_t pid, /*out*/ std::unique_ptr<File>* pagemap_file) { // Open /proc/<pid>/pagemap.
std::string pagemap_file_name = StringPrintf( "/proc/%ld/pagemap", static_cast<long>(pid)); // NOLINT [runtime/int] return open_file(pagemap_file_name.c_str(), pagemap_file);
};
// Open files for inspecting image memory.
std::vector<android::procinfo::MapInfo> image_proc_maps;
std::unique_ptr<File> image_mem_file;
std::unique_ptr<File> image_pagemap_file; if (!open_proc_maps(image_diff_pid_, &image_proc_maps) ||
!open_mem_file(image_diff_pid_, &image_mem_file) ||
!open_pagemap_file(image_diff_pid_, &image_pagemap_file)) { returnfalse;
}
// If zygote_diff_pid_ != -1, open files for inspecting zygote memory.
std::vector<android::procinfo::MapInfo> zygote_proc_maps;
std::unique_ptr<File> zygote_mem_file;
std::unique_ptr<File> zygote_pagemap_file; if (zygote_diff_pid_ != -1) { if (!open_proc_maps(zygote_diff_pid_, &zygote_proc_maps) ||
!open_mem_file(zygote_diff_pid_, &zygote_mem_file) ||
!open_pagemap_file(zygote_diff_pid_, &zygote_pagemap_file)) { returnfalse;
}
}
bool ComputeDirtyBytes(const ImageHeader& image_header, const android::procinfo::MapInfo& boot_map,
ArrayRef<uint8_t> remote_contents,
ArrayRef<uint8_t> zygote_contents,
MappingData* mapping_data /*out*/,
std::string* error_msg /*out*/) { // Iterate through one page at a time. Boot map begin/end already implicitly aligned. for (uintptr_t begin = boot_map.start; begin != boot_map.end; begin += MemMap::GetPageSize()) { const ptrdiff_t offset = begin - boot_map.start;
// We treat the image header as part of the memory map for now // If we wanted to change this, we could pass base=start+sizeof(ImageHeader) // But it might still be interesting to see if any of the ImageHeader data mutated const uint8_t* zygote_ptr = &zygote_contents[offset]; const uint8_t* remote_ptr = &remote_contents[offset];
if (memcmp(zygote_ptr, remote_ptr, MemMap::GetPageSize()) != 0) {
mapping_data->different_pages++;
// Count the number of 32-bit integers that are different. for (size_t i = 0; i < MemMap::GetPageSize() / sizeof(uint32_t); ++i) { const uint32_t* remote_ptr_int32 = reinterpret_cast<const uint32_t*>(remote_ptr); const uint32_t* zygote_ptr_int32 = reinterpret_cast<const uint32_t*>(zygote_ptr);
if (remote_ptr_int32[i] != zygote_ptr_int32[i]) {
mapping_data->different_int32s++;
}
} // Count the number of bytes that are different. for (size_t i = 0; i < MemMap::GetPageSize(); ++i) { if (remote_ptr[i] != zygote_ptr[i]) {
mapping_data->different_bytes++;
}
}
}
}
for (uintptr_t begin = boot_map.start; begin != boot_map.end; begin += MemMap::GetPageSize()) {
ptrdiff_t offset = begin - boot_map.start;
// Virtual page number (for an absolute memory address)
size_t virtual_page_idx = begin / MemMap::GetPageSize();
uint64_t page_count = 0xC0FFEE; // TODO: virtual_page_idx needs to be from the same process int dirtiness = (IsPageDirty(image_pagemap_file_, // Image-diff-pid procmap
zygote_pagemap_file_, // Zygote procmap
kpageflags_file_,
kpagecount_file_,
virtual_page_idx, // compare same page in image
virtual_page_idx, // and zygote /*out*/ page_count, /*out*/ *error_msg)); if (dirtiness < 0) { returnfalse;
} elseif (dirtiness > 0) {
mapping_data->dirty_pages++;
mapping_data->dirty_page_set.insert(mapping_data->dirty_page_set.end(), virtual_page_idx);
}
size_t total_private_dirty_pages = std::accumulate(
mapping_data.private_dirty_pages_for_section.begin(),
mapping_data.private_dirty_pages_for_section.end(), 0u);
os << "Image sections (total private dirty pages " << total_private_dirty_pages << ")\n"; for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) { const ImageHeader::ImageSections section = static_cast<ImageHeader::ImageSections>(i);
os << section << " " << image_header.GetImageSection(section)
<< " private dirty pages=" << mapping_data.private_dirty_pages_for_section[i] << "\n";
}
os << "\n";
}
// Look at /proc/$pid/mem and only diff the things from there bool DumpImageDiffMap(const ImageHeader& image_header, const std::string& image_location, const ParentMap& parent_map) REQUIRES_SHARED(Locks::mutator_lock_) {
std::ostream& os = *os_;
std::string error_msg;
std::string image_location_base_name = GetImageLocationBaseName(image_location); auto find_boot_map = [&os, &image_location_base_name]( const std::vector<android::procinfo::MapInfo>& maps, constchar* tag) -> std::optional<android::procinfo::MapInfo> { // Find the memory map for the current boot image component. for (const android::procinfo::MapInfo& map_info : maps) { // The map name ends with ']' if it's an anonymous memmap. We need to special case that // to find the boot image map in some cases. if (map_info.name.ends_with(image_location_base_name) ||
map_info.name.ends_with(image_location_base_name + "]")) { if ((map_info.flags & PROT_WRITE) != 0) { return map_info;
} // In actuality there's more than 1 map, but the second one is read-only. // The one we care about is the write-able map. // The readonly maps are guaranteed to be identical, so its not interesting to compare // them.
}
}
os << "Could not find map for " << image_location_base_name << " in " << tag; return std::nullopt;
};
// Find the current boot image mapping.
std::optional<android::procinfo::MapInfo> maybe_boot_map =
find_boot_map(image_proc_maps_, "image"); if (!maybe_boot_map) { returnfalse;
}
android::procinfo::MapInfo& boot_map = *maybe_boot_map; // Check the validity of the boot_map_.
CHECK(boot_map.end >= boot_map.start);
// Adjust the `end` of the mapping. Some other mappings may have been // inserted within the image.
boot_map.end = RoundUp(boot_map.start + image_header.GetImageSize(), MemMap::GetPageSize()); // The size of the boot image mapping.
size_t boot_map_size = boot_map.end - boot_map.start;
// If zygote_diff_pid_ != -1, check that the zygote boot map is the same. if (zygote_diff_pid_ != -1) {
std::optional<android::procinfo::MapInfo> maybe_zygote_boot_map =
find_boot_map(zygote_proc_maps_, "zygote"); if (!maybe_zygote_boot_map) { returnfalse;
}
android::procinfo::MapInfo& zygote_boot_map = *maybe_zygote_boot_map; // Adjust the `end` of the mapping. Some other mappings may have been // inserted within the image.
zygote_boot_map.end = RoundUp(zygote_boot_map.start + image_header.GetImageSize(),
MemMap::GetPageSize()); if (zygote_boot_map.start != boot_map.start) {
os << "Zygote boot map does not match image boot map: "
<< "zygote begin " << reinterpret_cast<constvoid*>(zygote_boot_map.start)
<< ", zygote end " << reinterpret_cast<constvoid*>(zygote_boot_map.end)
<< ", image begin " << reinterpret_cast<constvoid*>(boot_map.start)
<< ", image end " << reinterpret_cast<constvoid*>(boot_map.end); returnfalse;
}
}
// Walk the bytes and diff against our boot image
os << "\nObserving boot image header at address "
<< reinterpret_cast<constvoid*>(&image_header)
<< "\n\n";
// Adjust range to nearest page const uint8_t* image_begin = AlignDown(image_begin_unaligned, MemMap::GetPageSize()); const uint8_t* image_end = AlignUp(image_end_unaligned, MemMap::GetPageSize());
size_t image_size = image_end - image_begin; if (image_size != boot_map_size) {
os << "Remote boot map size does not match local boot map size: "
<< "local size " << image_size
<< ", remote size " << boot_map_size; returnfalse;
}
auto read_contents = [&](File* mem_file, /*out*/ MemMap* map, /*out*/ ArrayRef<uint8_t>* contents) {
DCHECK_ALIGNED_PARAM(boot_map.start, MemMap::GetPageSize());
DCHECK_ALIGNED_PARAM(boot_map_size, MemMap::GetPageSize());
std::string name = "Contents of " + mem_file->GetPath();
std::string local_error_msg; // We need to use low 4 GiB memory so that we can walk the objects using standard // functions that use ObjPtr<> which is checking that it fits into lower 4 GiB.
*map = MemMap::MapAnonymous(name.c_str(),
boot_map_size,
PROT_READ | PROT_WRITE, /* low_4gb= */ true,
&local_error_msg); if (!map->IsValid()) {
os << "Failed to allocate anonymous mapping for " << boot_map_size << " bytes.\n"; returnfalse;
} if (!mem_file->PreadFully(map->Begin(), boot_map_size, boot_map.start)) {
os << "Could not fully read file " << image_mem_file_.GetPath(); returnfalse;
}
*contents = ArrayRef<uint8_t>(map->Begin(), boot_map_size); returntrue;
}; // The contents of /proc/<image_diff_pid_>/mem.
MemMap remote_contents_map;
ArrayRef<uint8_t> remote_contents; if (!read_contents(&image_mem_file_, &remote_contents_map, &remote_contents)) { returnfalse;
} // The contents of /proc/<zygote_diff_pid_>/mem.
MemMap zygote_contents_map;
ArrayRef<uint8_t> zygote_contents; if (zygote_diff_pid_ != -1) { if (!read_contents(&zygote_mem_file_, &zygote_contents_map, &zygote_contents)) { returnfalse;
}
}
// TODO: We need to update the entire diff to work with the ASLR. b/77856493 // Since the images may be relocated, just check the sizes. if (static_cast<uintptr_t>(image_end - image_begin) != boot_map.end - boot_map.start) {
os << "Remote boot map is a different size than local boot map: " << "local begin " << reinterpret_cast<constvoid*>(image_begin) << ", local end " << reinterpret_cast<constvoid*>(image_end) << ", remote begin " << reinterpret_cast<constvoid*>(boot_map.start) << ", remote end " << reinterpret_cast<constvoid*>(boot_map.end); returnfalse; // For more validation should also check the ImageHeader from the file
}
// Only app vs zygote is supported at the moment
CHECK_EQ(remotes, RemoteProcesses::kImageAndZygote);
MappingData mapping_data; if (!ComputeDirtyBytes(image_header,
boot_map,
remote_contents,
zygote_contents,
&mapping_data,
&error_msg)) {
os << error_msg; returnfalse;
}
os << "Mapping at [" << reinterpret_cast<void*>(boot_map.start) << ", "
<< reinterpret_cast<void*>(boot_map.end) << ") had:\n ";
PrintMappingData(mapping_data, image_header);
// Check all the mirror::Object entries in the image.
RegionData<mirror::Object> object_region_data(os_,
remote_contents,
zygote_contents,
boot_map,
image_header,
parent_map,
dump_dirty_objects_);
object_region_data.ProcessRegion(mapping_data,
remotes,
image_begin_unaligned);
// Check all the ArtMethod entries in the image.
RegionData<ArtMethod> artmethod_region_data(os_,
remote_contents,
zygote_contents,
boot_map,
image_header,
parent_map,
dump_dirty_objects_);
artmethod_region_data.ProcessRegion(mapping_data,
remotes,
image_begin_unaligned); returntrue;
}
staticint IsPageDirty(File& page_map_file,
File& clean_pagemap_file,
File& kpageflags_file,
File& kpagecount_file,
size_t virtual_page_idx,
size_t clean_virtual_page_idx, // Out parameters:
uint64_t& page_count,
std::string& error_msg) { // Check that files are not the same. Note that actual file paths can be equal, such as in // ImgDiagTest.ImageDiffPidSelf, where imgdiag compares memory pages against itself. // CHECK_NE(page_map_file.GetPath(), clean_pagemap_file.GetPath());
CHECK_NE(&page_map_file, &clean_pagemap_file);
// Read 64-bit entry from /proc/kpageflags to get the dirty bit for a page
uint64_t kpage_flags_entry = 0; if (!GetPageFlagsOrCount(
kpageflags_file, page_frame_number, /*out*/ kpage_flags_entry, error_msg)) { return -1;
}
// Read 64-bit entyry from /proc/kpagecount to get mapping counts for a page if (!GetPageFlagsOrCount(kpagecount_file, page_frame_number, /*out*/ page_count, error_msg)) { return -1;
}
// There must be a page frame at the requested address.
CHECK_EQ(kpage_flags_entry & kPageFlagsNoPageMask, 0u); // The page frame must be memory mapped
CHECK_NE(kpage_flags_entry & kPageFlagsMmapMask, 0u);
// Return suffix of the file path after the last /. (e.g. /foo/bar -> bar, bar -> bar) static std::string BaseName(const std::string& str) {
size_t idx = str.rfind('/'); if (idx == std::string::npos) { return str;
}
return str.substr(idx + 1);
}
// Return the image location, stripped of any directories, e.g. "boot.art" static std::string GetImageLocationBaseName(const std::string& image_location) { return BaseName(std::string(image_location));
}
std::ostream* os_;
pid_t image_diff_pid_; // Dump image diff against boot.art if pid is non-negative
pid_t zygote_diff_pid_; // Dump image diff against zygote boot.art if pid is non-negative bool dump_dirty_objects_; // Adds dumping of objects that are dirty. bool zygote_pid_only_; // The user only specified a pid for the zygote.
// Used for finding the memory mapping of the image file.
std::vector<android::procinfo::MapInfo> image_proc_maps_; // A File for reading /proc/<image_diff_pid_>/mem.
File image_mem_file_; // A File for reading /proc/<image_diff_pid_>/pagemap.
File image_pagemap_file_;
// Used for finding the memory mapping of the zygote image file.
std::vector<android::procinfo::MapInfo> zygote_proc_maps_; // A File for reading /proc/<zygote_diff_pid_>/mem.
File zygote_mem_file_; // A File for reading /proc/<zygote_diff_pid_>/pagemap.
File zygote_pagemap_file_;
// A File for reading /proc/kpageflags.
File kpageflags_file_; // A File for reading /proc/kpagecount.
File kpagecount_file_;
if (kill(image_diff_pid_, /*sig*/0) != 0) { // No signal is sent, perform error-checking only. // Check if the pid exists before proceeding. if (errno == ESRCH) {
*error_msg = "Process specified does not exist";
} else {
*error_msg = StringPrintf("Failed to check process status: %s", strerror(errno));
} return kParseError;
} elseif (instruction_set_ != InstructionSet::kNone && instruction_set_ != kRuntimeISA) { // Don't allow different ISAs since the images are ISA-specific. // Right now the code assumes both the runtime ISA and the remote ISA are identical.
*error_msg = "Must use the default runtime ISA; changing ISA is not supported."; return kParseError;
}
usage += // Optional. " --image-diff-pid=<pid>: provide the PID of a process whose boot.art you want to diff.\n" " Example: --image-diff-pid=$(pid zygote)\n" " --zygote-diff-pid=<pid>: provide the PID of the zygote whose boot.art you want to diff " "against.\n" " Example: --zygote-diff-pid=$(pid zygote)\n" " --dump-dirty-objects: additionally output dirty objects of interest.\n" "\n";
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.