1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <stdio.h> 18 #include <stdlib.h> 19 20 #include <fstream> 21 #include <functional> 22 #include <iostream> 23 #include <string> 24 #include <vector> 25 #include <set> 26 #include <map> 27 #include <unordered_set> 28 29 #include "android-base/stringprintf.h" 30 31 #include "art_field-inl.h" 32 #include "art_method-inl.h" 33 #include "base/unix_file/fd_file.h" 34 #include "class_linker.h" 35 #include "gc/space/image_space.h" 36 #include "gc/heap.h" 37 #include "mirror/class-inl.h" 38 #include "mirror/object-inl.h" 39 #include "image.h" 40 #include "oat.h" 41 #include "oat_file.h" 42 #include "oat_file_manager.h" 43 #include "os.h" 44 #include "scoped_thread_state_change-inl.h" 45 46 #include "cmdline.h" 47 #include "backtrace/BacktraceMap.h" 48 49 #include <sys/stat.h> 50 #include <sys/types.h> 51 #include <signal.h> 52 53 namespace art { 54 55 using android::base::StringPrintf; 56 57 namespace { 58 59 constexpr size_t kMaxAddressPrint = 5; 60 61 enum class ProcessType { 62 kZygote, 63 kRemote 64 }; 65 66 enum class RemoteProcesses { 67 kImageOnly, 68 kZygoteOnly, 69 kImageAndZygote 70 }; 71 72 struct MappingData { 73 // The count of pages that are considered dirty by the OS. 74 size_t dirty_pages = 0; 75 // The count of pages that differ by at least one byte. 76 size_t different_pages = 0; 77 // The count of differing bytes. 78 size_t different_bytes = 0; 79 // The count of differing four-byte units. 80 size_t different_int32s = 0; 81 // The count of pages that have mapping count == 1. 82 size_t private_pages = 0; 83 // The count of private pages that are also dirty. 84 size_t private_dirty_pages = 0; 85 // The count of pages that are marked dirty but do not differ. 86 size_t false_dirty_pages = 0; 87 // Set of the local virtual page indices that are dirty. 88 std::set<size_t> dirty_page_set; 89 }; 90 91 static std::string GetClassDescriptor(mirror::Class* klass) 92 REQUIRES_SHARED(Locks::mutator_lock_) { 93 CHECK(klass != nullptr); 94 95 std::string descriptor; 96 const char* descriptor_str = klass->GetDescriptor(&descriptor /*out*/); 97 98 return std::string(descriptor_str); 99 } 100 101 static std::string PrettyFieldValue(ArtField* field, mirror::Object* object) 102 REQUIRES_SHARED(Locks::mutator_lock_) { 103 std::ostringstream oss; 104 switch (field->GetTypeAsPrimitiveType()) { 105 case Primitive::kPrimNot: { 106 oss << object->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>( 107 field->GetOffset()); 108 break; 109 } 110 case Primitive::kPrimBoolean: { 111 oss << static_cast<bool>(object->GetFieldBoolean<kVerifyNone>(field->GetOffset())); 112 break; 113 } 114 case Primitive::kPrimByte: { 115 oss << static_cast<int32_t>(object->GetFieldByte<kVerifyNone>(field->GetOffset())); 116 break; 117 } 118 case Primitive::kPrimChar: { 119 oss << object->GetFieldChar<kVerifyNone>(field->GetOffset()); 120 break; 121 } 122 case Primitive::kPrimShort: { 123 oss << object->GetFieldShort<kVerifyNone>(field->GetOffset()); 124 break; 125 } 126 case Primitive::kPrimInt: { 127 oss << object->GetField32<kVerifyNone>(field->GetOffset()); 128 break; 129 } 130 case Primitive::kPrimLong: { 131 oss << object->GetField64<kVerifyNone>(field->GetOffset()); 132 break; 133 } 134 case Primitive::kPrimFloat: { 135 oss << object->GetField32<kVerifyNone>(field->GetOffset()); 136 break; 137 } 138 case Primitive::kPrimDouble: { 139 oss << object->GetField64<kVerifyNone>(field->GetOffset()); 140 break; 141 } 142 case Primitive::kPrimVoid: { 143 oss << "void"; 144 break; 145 } 146 } 147 return oss.str(); 148 } 149 150 template <typename K, typename V, typename D> 151 static std::vector<std::pair<V, K>> SortByValueDesc( 152 const std::map<K, D> map, 153 std::function<V(const D&)> value_mapper = [](const D& d) { return static_cast<V>(d); }) { 154 // Store value->key so that we can use the default sort from pair which 155 // sorts by value first and then key 156 std::vector<std::pair<V, K>> value_key_vector; 157 158 for (const auto& kv_pair : map) { 159 value_key_vector.push_back(std::make_pair(value_mapper(kv_pair.second), kv_pair.first)); 160 } 161 162 // Sort in reverse (descending order) 163 std::sort(value_key_vector.rbegin(), value_key_vector.rend()); 164 return value_key_vector; 165 } 166 167 // Fixup a remote pointer that we read from a foreign boot.art to point to our own memory. 168 // Returned pointer will point to inside of remote_contents. 169 template <typename T> 170 static T* FixUpRemotePointer(T* remote_ptr, 171 std::vector<uint8_t>& remote_contents, 172 const backtrace_map_t& boot_map) { 173 if (remote_ptr == nullptr) { 174 return nullptr; 175 } 176 177 uintptr_t remote = reinterpret_cast<uintptr_t>(remote_ptr); 178 179 CHECK_LE(boot_map.start, remote); 180 CHECK_GT(boot_map.end, remote); 181 182 off_t boot_offset = remote - boot_map.start; 183 184 return reinterpret_cast<T*>(&remote_contents[boot_offset]); 185 } 186 187 template <typename T> 188 static T* RemoteContentsPointerToLocal(T* remote_ptr, 189 std::vector<uint8_t>& remote_contents, 190 const ImageHeader& image_header) { 191 if (remote_ptr == nullptr) { 192 return nullptr; 193 } 194 195 uint8_t* remote = reinterpret_cast<uint8_t*>(remote_ptr); 196 ptrdiff_t boot_offset = remote - &remote_contents[0]; 197 198 const uint8_t* local_ptr = reinterpret_cast<const uint8_t*>(&image_header) + boot_offset; 199 200 return reinterpret_cast<T*>(const_cast<uint8_t*>(local_ptr)); 201 } 202 203 template <typename T> size_t EntrySize(T* entry); 204 template<> size_t EntrySize(mirror::Object* object) REQUIRES_SHARED(Locks::mutator_lock_) { 205 return object->SizeOf(); 206 } 207 template<> size_t EntrySize(ArtMethod* art_method) REQUIRES_SHARED(Locks::mutator_lock_) { 208 return sizeof(*art_method); 209 } 210 211 template <typename T> 212 static bool EntriesDiffer(T* entry1, T* entry2) REQUIRES_SHARED(Locks::mutator_lock_) { 213 return memcmp(entry1, entry2, EntrySize(entry1)) != 0; 214 } 215 216 template <typename T> 217 struct RegionCommon { 218 public: 219 RegionCommon(std::ostream* os, 220 std::vector<uint8_t>* remote_contents, 221 std::vector<uint8_t>* zygote_contents, 222 const backtrace_map_t& boot_map, 223 const ImageHeader& image_header) : 224 os_(*os), 225 remote_contents_(remote_contents), 226 zygote_contents_(zygote_contents), 227 boot_map_(boot_map), 228 image_header_(image_header), 229 different_entries_(0), 230 dirty_entry_bytes_(0), 231 false_dirty_entry_bytes_(0) { 232 CHECK(remote_contents != nullptr); 233 CHECK(zygote_contents != nullptr); 234 } 235 236 void DumpSamplesAndOffsetCount() { 237 os_ << " sample object addresses: "; 238 for (size_t i = 0; i < dirty_entries_.size() && i < kMaxAddressPrint; ++i) { 239 T* entry = dirty_entries_[i]; 240 os_ << reinterpret_cast<void*>(entry) << ", "; 241 } 242 os_ << "\n"; 243 os_ << " dirty byte +offset:count list = "; 244 std::vector<std::pair<size_t, off_t>> field_dirty_count_sorted = 245 SortByValueDesc<off_t, size_t, size_t>(field_dirty_count_); 246 for (const std::pair<size_t, off_t>& pair : field_dirty_count_sorted) { 247 off_t offset = pair.second; 248 size_t count = pair.first; 249 os_ << "+" << offset << ":" << count << ", "; 250 } 251 os_ << "\n"; 252 } 253 254 size_t GetDifferentEntryCount() const { return different_entries_; } 255 size_t GetDirtyEntryBytes() const { return dirty_entry_bytes_; } 256 size_t GetFalseDirtyEntryCount() const { return false_dirty_entries_.size(); } 257 size_t GetFalseDirtyEntryBytes() const { return false_dirty_entry_bytes_; } 258 size_t GetZygoteDirtyEntryCount() const { return zygote_dirty_entries_.size(); } 259 260 protected: 261 bool IsEntryOnDirtyPage(T* entry, const std::set<size_t>& dirty_pages) const 262 REQUIRES_SHARED(Locks::mutator_lock_) { 263 size_t size = EntrySize(entry); 264 size_t page_off = 0; 265 size_t current_page_idx; 266 uintptr_t entry_address = reinterpret_cast<uintptr_t>(entry); 267 // Iterate every page this entry belongs to 268 do { 269 current_page_idx = entry_address / kPageSize + page_off; 270 if (dirty_pages.find(current_page_idx) != dirty_pages.end()) { 271 // This entry is on a dirty page 272 return true; 273 } 274 page_off++; 275 } while ((current_page_idx * kPageSize) < RoundUp(entry_address + size, kObjectAlignment)); 276 return false; 277 } 278 279 void AddZygoteDirtyEntry(T* entry) REQUIRES_SHARED(Locks::mutator_lock_) { 280 zygote_dirty_entries_.insert(entry); 281 } 282 283 void AddImageDirtyEntry(T* entry) REQUIRES_SHARED(Locks::mutator_lock_) { 284 image_dirty_entries_.insert(entry); 285 } 286 287 void AddFalseDirtyEntry(T* entry) REQUIRES_SHARED(Locks::mutator_lock_) { 288 false_dirty_entries_.push_back(entry); 289 false_dirty_entry_bytes_ += EntrySize(entry); 290 } 291 292 // The output stream to write to. 293 std::ostream& os_; 294 // The byte contents of the remote (image) process' image. 295 std::vector<uint8_t>* remote_contents_; 296 // The byte contents of the zygote process' image. 297 std::vector<uint8_t>* zygote_contents_; 298 const backtrace_map_t& boot_map_; 299 const ImageHeader& image_header_; 300 301 // Count of entries that are different. 302 size_t different_entries_; 303 304 // Local entries that are dirty (differ in at least one byte). 305 size_t dirty_entry_bytes_; 306 std::vector<T*> dirty_entries_; 307 308 // Local entries that are clean, but located on dirty pages. 309 size_t false_dirty_entry_bytes_; 310 std::vector<T*> false_dirty_entries_; 311 312 // Image dirty entries 313 // If zygote_pid_only_ == true, these are shared dirty entries in the zygote. 314 // If zygote_pid_only_ == false, these are private dirty entries in the application. 315 std::set<T*> image_dirty_entries_; 316 317 // Zygote dirty entries (probably private dirty). 318 // We only add entries here if they differed in both the image and the zygote, so 319 // they are probably private dirty. 320 std::set<T*> zygote_dirty_entries_; 321 322 std::map<off_t /* field offset */, size_t /* count */> field_dirty_count_; 323 324 private: 325 DISALLOW_COPY_AND_ASSIGN(RegionCommon); 326 }; 327 328 template <typename T> 329 class RegionSpecializedBase : public RegionCommon<T> { 330 }; 331 332 // Region analysis for mirror::Objects 333 class ImgObjectVisitor : public ObjectVisitor { 334 public: 335 using ComputeDirtyFunc = std::function<void(mirror::Object* object, 336 const uint8_t* begin_image_ptr, 337 const std::set<size_t>& dirty_pages)>; 338 ImgObjectVisitor(ComputeDirtyFunc dirty_func, 339 const uint8_t* begin_image_ptr, 340 const std::set<size_t>& dirty_pages) : 341 dirty_func_(dirty_func), 342 begin_image_ptr_(begin_image_ptr), 343 dirty_pages_(dirty_pages) { } 344 345 virtual ~ImgObjectVisitor() OVERRIDE { } 346 347 virtual void Visit(mirror::Object* object) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { 348 // Sanity check that we are reading a real mirror::Object 349 CHECK(object->GetClass() != nullptr) << "Image object at address " 350 << object 351 << " has null class"; 352 if (kUseBakerReadBarrier) { 353 object->AssertReadBarrierState(); 354 } 355 dirty_func_(object, begin_image_ptr_, dirty_pages_); 356 } 357 358 private: 359 ComputeDirtyFunc dirty_func_; 360 const uint8_t* begin_image_ptr_; 361 const std::set<size_t>& dirty_pages_; 362 }; 363 364 template<> 365 class RegionSpecializedBase<mirror::Object> : public RegionCommon<mirror::Object> { 366 public: 367 RegionSpecializedBase(std::ostream* os, 368 std::vector<uint8_t>* remote_contents, 369 std::vector<uint8_t>* zygote_contents, 370 const backtrace_map_t& boot_map, 371 const ImageHeader& image_header, 372 bool dump_dirty_objects) 373 : RegionCommon<mirror::Object>(os, remote_contents, zygote_contents, boot_map, image_header), 374 os_(*os), 375 dump_dirty_objects_(dump_dirty_objects) { } 376 377 // Define a common public type name for use by RegionData. 378 using VisitorClass = ImgObjectVisitor; 379 380 void VisitEntries(VisitorClass* visitor, 381 uint8_t* base, 382 PointerSize pointer_size) 383 REQUIRES_SHARED(Locks::mutator_lock_) { 384 RegionCommon<mirror::Object>::image_header_.VisitObjects(visitor, base, pointer_size); 385 } 386 387 void VisitEntry(mirror::Object* entry) 388 REQUIRES_SHARED(Locks::mutator_lock_) { 389 // Unconditionally store the class descriptor in case we need it later 390 mirror::Class* klass = entry->GetClass(); 391 class_data_[klass].descriptor = GetClassDescriptor(klass); 392 } 393 394 void AddCleanEntry(mirror::Object* entry) 395 REQUIRES_SHARED(Locks::mutator_lock_) { 396 class_data_[entry->GetClass()].AddCleanObject(); 397 } 398 399 void AddFalseDirtyEntry(mirror::Object* entry) 400 REQUIRES_SHARED(Locks::mutator_lock_) { 401 RegionCommon<mirror::Object>::AddFalseDirtyEntry(entry); 402 class_data_[entry->GetClass()].AddFalseDirtyObject(entry); 403 } 404 405 void AddDirtyEntry(mirror::Object* entry, mirror::Object* entry_remote) 406 REQUIRES_SHARED(Locks::mutator_lock_) { 407 size_t entry_size = EntrySize(entry); 408 ++different_entries_; 409 dirty_entry_bytes_ += entry_size; 410 // Log dirty count and objects for class objects only. 411 mirror::Class* klass = entry->GetClass(); 412 if (klass->IsClassClass()) { 413 // Increment counts for the fields that are dirty 414 const uint8_t* current = reinterpret_cast<const uint8_t*>(entry); 415 const uint8_t* current_remote = reinterpret_cast<const uint8_t*>(entry_remote); 416 for (size_t i = 0; i < entry_size; ++i) { 417 if (current[i] != current_remote[i]) { 418 field_dirty_count_[i]++; 419 } 420 } 421 dirty_entries_.push_back(entry); 422 } 423 class_data_[klass].AddDirtyObject(entry, entry_remote); 424 } 425 426 void DiffEntryContents(mirror::Object* entry, 427 uint8_t* remote_bytes, 428 const uint8_t* base_ptr, 429 bool log_dirty_objects) 430 REQUIRES_SHARED(Locks::mutator_lock_) { 431 const char* tabs = " "; 432 // Attempt to find fields for all dirty bytes. 433 mirror::Class* klass = entry->GetClass(); 434 if (entry->IsClass()) { 435 os_ << tabs 436 << "Class " << mirror::Class::PrettyClass(entry->AsClass()) << " " << entry << "\n"; 437 } else { 438 os_ << tabs 439 << "Instance of " << mirror::Class::PrettyClass(klass) << " " << entry << "\n"; 440 } 441 442 std::unordered_set<ArtField*> dirty_instance_fields; 443 std::unordered_set<ArtField*> dirty_static_fields; 444 // Examine the bytes comprising the Object, computing which fields are dirty 445 // and recording them for later display. If the Object is an array object, 446 // compute the dirty entries. 447 mirror::Object* remote_entry = reinterpret_cast<mirror::Object*>(remote_bytes); 448 for (size_t i = 0, count = entry->SizeOf(); i < count; ++i) { 449 if (base_ptr[i] != remote_bytes[i]) { 450 ArtField* field = ArtField::FindInstanceFieldWithOffset</*exact*/false>(klass, i); 451 if (field != nullptr) { 452 dirty_instance_fields.insert(field); 453 } else if (entry->IsClass()) { 454 field = ArtField::FindStaticFieldWithOffset</*exact*/false>(entry->AsClass(), i); 455 if (field != nullptr) { 456 dirty_static_fields.insert(field); 457 } 458 } 459 if (field == nullptr) { 460 if (klass->IsArrayClass()) { 461 mirror::Class* component_type = klass->GetComponentType(); 462 Primitive::Type primitive_type = component_type->GetPrimitiveType(); 463 size_t component_size = Primitive::ComponentSize(primitive_type); 464 size_t data_offset = mirror::Array::DataOffset(component_size).Uint32Value(); 465 if (i >= data_offset) { 466 os_ << tabs << "Dirty array element " << (i - data_offset) / component_size << "\n"; 467 // Skip to next element to prevent spam. 468 i += component_size - 1; 469 continue; 470 } 471 } 472 os_ << tabs << "No field for byte offset " << i << "\n"; 473 } 474 } 475 } 476 // Dump different fields. 477 if (!dirty_instance_fields.empty()) { 478 os_ << tabs << "Dirty instance fields " << dirty_instance_fields.size() << "\n"; 479 for (ArtField* field : dirty_instance_fields) { 480 os_ << tabs << ArtField::PrettyField(field) 481 << " original=" << PrettyFieldValue(field, entry) 482 << " remote=" << PrettyFieldValue(field, remote_entry) << "\n"; 483 } 484 } 485 if (!dirty_static_fields.empty()) { 486 if (dump_dirty_objects_ && log_dirty_objects) { 487 dirty_objects_.insert(entry); 488 } 489 os_ << tabs << "Dirty static fields " << dirty_static_fields.size() << "\n"; 490 for (ArtField* field : dirty_static_fields) { 491 os_ << tabs << ArtField::PrettyField(field) 492 << " original=" << PrettyFieldValue(field, entry) 493 << " remote=" << PrettyFieldValue(field, remote_entry) << "\n"; 494 } 495 } 496 os_ << "\n"; 497 } 498 499 void DumpDirtyObjects() REQUIRES_SHARED(Locks::mutator_lock_) { 500 for (mirror::Object* obj : dirty_objects_) { 501 if (obj->IsClass()) { 502 os_ << "Private dirty object: " << obj->AsClass()->PrettyDescriptor() << "\n"; 503 } 504 } 505 } 506 507 void DumpDirtyEntries() REQUIRES_SHARED(Locks::mutator_lock_) { 508 // vector of pairs (size_t count, Class*) 509 auto dirty_object_class_values = 510 SortByValueDesc<mirror::Class*, size_t, ClassData>( 511 class_data_, 512 [](const ClassData& d) { return d.dirty_object_count; }); 513 os_ << "\n" << " Dirty object count by class:\n"; 514 for (const auto& vk_pair : dirty_object_class_values) { 515 size_t dirty_object_count = vk_pair.first; 516 mirror::Class* klass = vk_pair.second; 517 ClassData& class_data = class_data_[klass]; 518 size_t object_sizes = class_data.dirty_object_size_in_bytes; 519 float avg_dirty_bytes_per_class = 520 class_data.dirty_object_byte_count * 1.0f / object_sizes; 521 float avg_object_size = object_sizes * 1.0f / dirty_object_count; 522 const std::string& descriptor = class_data.descriptor; 523 os_ << " " << mirror::Class::PrettyClass(klass) << " (" 524 << "objects: " << dirty_object_count << ", " 525 << "avg dirty bytes: " << avg_dirty_bytes_per_class << ", " 526 << "avg object size: " << avg_object_size << ", " 527 << "class descriptor: '" << descriptor << "'" 528 << ")\n"; 529 if (strcmp(descriptor.c_str(), "Ljava/lang/Class;") == 0) { 530 DumpSamplesAndOffsetCount(); 531 os_ << " field contents:\n"; 532 for (mirror::Object* object : class_data.dirty_objects) { 533 // remote class object 534 auto remote_klass = reinterpret_cast<mirror::Class*>(object); 535 // local class object 536 auto local_klass = 537 RemoteContentsPointerToLocal(remote_klass, 538 *RegionCommon<mirror::Object>::remote_contents_, 539 RegionCommon<mirror::Object>::image_header_); 540 os_ << " " << reinterpret_cast<const void*>(object) << " "; 541 os_ << " class_status (remote): " << remote_klass->GetStatus() << ", "; 542 os_ << " class_status (local): " << local_klass->GetStatus(); 543 os_ << "\n"; 544 } 545 } 546 } 547 } 548 549 void DumpFalseDirtyEntries() REQUIRES_SHARED(Locks::mutator_lock_) { 550 // vector of pairs (size_t count, Class*) 551 auto false_dirty_object_class_values = 552 SortByValueDesc<mirror::Class*, size_t, ClassData>( 553 class_data_, 554 [](const ClassData& d) { return d.false_dirty_object_count; }); 555 os_ << "\n" << " False-dirty object count by class:\n"; 556 for (const auto& vk_pair : false_dirty_object_class_values) { 557 size_t object_count = vk_pair.first; 558 mirror::Class* klass = vk_pair.second; 559 ClassData& class_data = class_data_[klass]; 560 size_t object_sizes = class_data.false_dirty_byte_count; 561 float avg_object_size = object_sizes * 1.0f / object_count; 562 const std::string& descriptor = class_data.descriptor; 563 os_ << " " << mirror::Class::PrettyClass(klass) << " (" 564 << "objects: " << object_count << ", " 565 << "avg object size: " << avg_object_size << ", " 566 << "total bytes: " << object_sizes << ", " 567 << "class descriptor: '" << descriptor << "'" 568 << ")\n"; 569 } 570 } 571 572 void DumpCleanEntries() REQUIRES_SHARED(Locks::mutator_lock_) { 573 // vector of pairs (size_t count, Class*) 574 auto clean_object_class_values = 575 SortByValueDesc<mirror::Class*, size_t, ClassData>( 576 class_data_, 577 [](const ClassData& d) { return d.clean_object_count; }); 578 os_ << "\n" << " Clean object count by class:\n"; 579 for (const auto& vk_pair : clean_object_class_values) { 580 os_ << " " << mirror::Class::PrettyClass(vk_pair.second) << " (" << vk_pair.first << ")\n"; 581 } 582 } 583 584 private: 585 // Aggregate and detail class data from an image diff. 586 struct ClassData { 587 size_t dirty_object_count = 0; 588 // Track only the byte-per-byte dirtiness (in bytes) 589 size_t dirty_object_byte_count = 0; 590 // Track the object-by-object dirtiness (in bytes) 591 size_t dirty_object_size_in_bytes = 0; 592 size_t clean_object_count = 0; 593 std::string descriptor; 594 size_t false_dirty_byte_count = 0; 595 size_t false_dirty_object_count = 0; 596 std::vector<mirror::Object*> false_dirty_objects; 597 // Remote pointers to dirty objects 598 std::vector<mirror::Object*> dirty_objects; 599 600 void AddCleanObject() REQUIRES_SHARED(Locks::mutator_lock_) { 601 ++clean_object_count; 602 } 603 604 void AddDirtyObject(mirror::Object* object, mirror::Object* object_remote) 605 REQUIRES_SHARED(Locks::mutator_lock_) { 606 ++dirty_object_count; 607 dirty_object_byte_count += CountDirtyBytes(object, object_remote); 608 dirty_object_size_in_bytes += EntrySize(object); 609 dirty_objects.push_back(object_remote); 610 } 611 612 void AddFalseDirtyObject(mirror::Object* object) REQUIRES_SHARED(Locks::mutator_lock_) { 613 ++false_dirty_object_count; 614 false_dirty_objects.push_back(object); 615 false_dirty_byte_count += EntrySize(object); 616 } 617 618 private: 619 // Go byte-by-byte and figure out what exactly got dirtied 620 static size_t CountDirtyBytes(mirror::Object* object1, mirror::Object* object2) 621 REQUIRES_SHARED(Locks::mutator_lock_) { 622 const uint8_t* cur1 = reinterpret_cast<const uint8_t*>(object1); 623 const uint8_t* cur2 = reinterpret_cast<const uint8_t*>(object2); 624 size_t dirty_bytes = 0; 625 size_t object_size = EntrySize(object1); 626 for (size_t i = 0; i < object_size; ++i) { 627 if (cur1[i] != cur2[i]) { 628 dirty_bytes++; 629 } 630 } 631 return dirty_bytes; 632 } 633 }; 634 635 std::ostream& os_; 636 bool dump_dirty_objects_; 637 std::unordered_set<mirror::Object*> dirty_objects_; 638 std::map<mirror::Class*, ClassData> class_data_; 639 640 DISALLOW_COPY_AND_ASSIGN(RegionSpecializedBase); 641 }; 642 643 // Region analysis for ArtMethods. 644 class ImgArtMethodVisitor : public ArtMethodVisitor { 645 public: 646 using ComputeDirtyFunc = std::function<void(ArtMethod*, 647 const uint8_t*, 648 const std::set<size_t>&)>; 649 ImgArtMethodVisitor(ComputeDirtyFunc dirty_func, 650 const uint8_t* begin_image_ptr, 651 const std::set<size_t>& dirty_pages) : 652 dirty_func_(dirty_func), 653 begin_image_ptr_(begin_image_ptr), 654 dirty_pages_(dirty_pages) { } 655 virtual ~ImgArtMethodVisitor() OVERRIDE { } 656 virtual void Visit(ArtMethod* method) OVERRIDE { 657 dirty_func_(method, begin_image_ptr_, dirty_pages_); 658 } 659 660 private: 661 ComputeDirtyFunc dirty_func_; 662 const uint8_t* begin_image_ptr_; 663 const std::set<size_t>& dirty_pages_; 664 }; 665 666 // Struct and functor for computing offsets of members of ArtMethods. 667 // template <typename RegionType> 668 struct MemberInfo { 669 template <typename T> 670 void operator() (const ArtMethod* method, const T* member_address, const std::string& name) { 671 // Check that member_address is a pointer inside *method. 672 DCHECK(reinterpret_cast<uintptr_t>(method) <= reinterpret_cast<uintptr_t>(member_address)); 673 DCHECK(reinterpret_cast<uintptr_t>(member_address) + sizeof(T) <= 674 reinterpret_cast<uintptr_t>(method) + sizeof(ArtMethod)); 675 size_t offset = 676 reinterpret_cast<uintptr_t>(member_address) - reinterpret_cast<uintptr_t>(method); 677 offset_to_name_size_.insert({offset, NameAndSize(sizeof(T), name)}); 678 } 679 680 struct NameAndSize { 681 size_t size_; 682 std::string name_; 683 NameAndSize(size_t size, const std::string& name) : size_(size), name_(name) { } 684 NameAndSize() : size_(0), name_("INVALID") { } 685 }; 686 687 std::map<size_t, NameAndSize> offset_to_name_size_; 688 }; 689 690 template<> 691 class RegionSpecializedBase<ArtMethod> : public RegionCommon<ArtMethod> { 692 public: 693 RegionSpecializedBase(std::ostream* os, 694 std::vector<uint8_t>* remote_contents, 695 std::vector<uint8_t>* zygote_contents, 696 const backtrace_map_t& boot_map, 697 const ImageHeader& image_header, 698 bool dump_dirty_objects ATTRIBUTE_UNUSED) 699 : RegionCommon<ArtMethod>(os, remote_contents, zygote_contents, boot_map, image_header), 700 os_(*os) { 701 // Prepare the table for offset to member lookups. 702 ArtMethod* art_method = reinterpret_cast<ArtMethod*>(&(*remote_contents)[0]); 703 art_method->VisitMembers(member_info_); 704 // Prepare the table for address to symbolic entry point names. 705 BuildEntryPointNames(); 706 class_linker_ = Runtime::Current()->GetClassLinker(); 707 } 708 709 // Define a common public type name for use by RegionData. 710 using VisitorClass = ImgArtMethodVisitor; 711 712 void VisitEntries(VisitorClass* visitor, 713 uint8_t* base, 714 PointerSize pointer_size) 715 REQUIRES_SHARED(Locks::mutator_lock_) { 716 RegionCommon<ArtMethod>::image_header_.VisitPackedArtMethods(visitor, base, pointer_size); 717 } 718 719 void VisitEntry(ArtMethod* method ATTRIBUTE_UNUSED) 720 REQUIRES_SHARED(Locks::mutator_lock_) { 721 } 722 723 void AddCleanEntry(ArtMethod* method ATTRIBUTE_UNUSED) { 724 } 725 726 void AddFalseDirtyEntry(ArtMethod* method) 727 REQUIRES_SHARED(Locks::mutator_lock_) { 728 RegionCommon<ArtMethod>::AddFalseDirtyEntry(method); 729 } 730 731 void AddDirtyEntry(ArtMethod* method, ArtMethod* method_remote) 732 REQUIRES_SHARED(Locks::mutator_lock_) { 733 size_t entry_size = EntrySize(method); 734 ++different_entries_; 735 dirty_entry_bytes_ += entry_size; 736 // Increment counts for the fields that are dirty 737 const uint8_t* current = reinterpret_cast<const uint8_t*>(method); 738 const uint8_t* current_remote = reinterpret_cast<const uint8_t*>(method_remote); 739 // ArtMethods always log their dirty count and entries. 740 for (size_t i = 0; i < entry_size; ++i) { 741 if (current[i] != current_remote[i]) { 742 field_dirty_count_[i]++; 743 } 744 } 745 dirty_entries_.push_back(method); 746 } 747 748 void DiffEntryContents(ArtMethod* method, 749 uint8_t* remote_bytes, 750 const uint8_t* base_ptr, 751 bool log_dirty_objects ATTRIBUTE_UNUSED) 752 REQUIRES_SHARED(Locks::mutator_lock_) { 753 const char* tabs = " "; 754 os_ << tabs << "ArtMethod " << ArtMethod::PrettyMethod(method) << "\n"; 755 756 std::unordered_set<size_t> dirty_members; 757 // Examine the members comprising the ArtMethod, computing which members are dirty. 758 for (const std::pair<size_t, MemberInfo::NameAndSize>& p : member_info_.offset_to_name_size_) { 759 const size_t offset = p.first; 760 if (memcmp(base_ptr + offset, remote_bytes + offset, p.second.size_) != 0) { 761 dirty_members.insert(p.first); 762 } 763 } 764 // Dump different fields. 765 if (!dirty_members.empty()) { 766 os_ << tabs << "Dirty members " << dirty_members.size() << "\n"; 767 for (size_t offset : dirty_members) { 768 const MemberInfo::NameAndSize& member_info = member_info_.offset_to_name_size_[offset]; 769 os_ << tabs << member_info.name_ 770 << " original=" << StringFromBytes(base_ptr + offset, member_info.size_) 771 << " remote=" << StringFromBytes(remote_bytes + offset, member_info.size_) 772 << "\n"; 773 } 774 } 775 os_ << "\n"; 776 } 777 778 void DumpDirtyObjects() REQUIRES_SHARED(Locks::mutator_lock_) { 779 } 780 781 void DumpDirtyEntries() REQUIRES_SHARED(Locks::mutator_lock_) { 782 DumpSamplesAndOffsetCount(); 783 os_ << " offset to field map:\n"; 784 for (const std::pair<size_t, MemberInfo::NameAndSize>& p : member_info_.offset_to_name_size_) { 785 const size_t offset = p.first; 786 const size_t size = p.second.size_; 787 os_ << StringPrintf(" %zu-%zu: ", offset, offset + size - 1) 788 << p.second.name_ 789 << std::endl; 790 } 791 792 os_ << " field contents:\n"; 793 for (ArtMethod* method : dirty_entries_) { 794 // remote method 795 auto art_method = reinterpret_cast<ArtMethod*>(method); 796 // remote class 797 mirror::Class* remote_declaring_class = 798 FixUpRemotePointer(art_method->GetDeclaringClass(), 799 *RegionCommon<ArtMethod>::remote_contents_, 800 RegionCommon<ArtMethod>::boot_map_); 801 // local class 802 mirror::Class* declaring_class = 803 RemoteContentsPointerToLocal(remote_declaring_class, 804 *RegionCommon<ArtMethod>::remote_contents_, 805 RegionCommon<ArtMethod>::image_header_); 806 DumpOneArtMethod(art_method, declaring_class, remote_declaring_class); 807 } 808 } 809 810 void DumpFalseDirtyEntries() REQUIRES_SHARED(Locks::mutator_lock_) { 811 os_ << "\n" << " False-dirty ArtMethods\n"; 812 os_ << " field contents:\n"; 813 for (ArtMethod* method : false_dirty_entries_) { 814 // local class 815 mirror::Class* declaring_class = method->GetDeclaringClass(); 816 DumpOneArtMethod(method, declaring_class, nullptr); 817 } 818 } 819 820 void DumpCleanEntries() REQUIRES_SHARED(Locks::mutator_lock_) { 821 } 822 823 private: 824 std::ostream& os_; 825 MemberInfo member_info_; 826 std::map<const void*, std::string> entry_point_names_; 827 ClassLinker* class_linker_; 828 829 // Compute a map of addresses to names in the boot OAT file(s). 830 void BuildEntryPointNames() { 831 OatFileManager& oat_file_manager = Runtime::Current()->GetOatFileManager(); 832 std::vector<const OatFile*> boot_oat_files = oat_file_manager.GetBootOatFiles(); 833 for (const OatFile* oat_file : boot_oat_files) { 834 const OatHeader& oat_header = oat_file->GetOatHeader(); 835 const void* i2ib = oat_header.GetInterpreterToInterpreterBridge(); 836 if (i2ib != nullptr) { 837 entry_point_names_[i2ib] = "InterpreterToInterpreterBridge (from boot oat file)"; 838 } 839 const void* i2ccb = oat_header.GetInterpreterToCompiledCodeBridge(); 840 if (i2ccb != nullptr) { 841 entry_point_names_[i2ccb] = "InterpreterToCompiledCodeBridge (from boot oat file)"; 842 } 843 const void* jdl = oat_header.GetJniDlsymLookup(); 844 if (jdl != nullptr) { 845 entry_point_names_[jdl] = "JniDlsymLookup (from boot oat file)"; 846 } 847 const void* qgjt = oat_header.GetQuickGenericJniTrampoline(); 848 if (qgjt != nullptr) { 849 entry_point_names_[qgjt] = "QuickGenericJniTrampoline (from boot oat file)"; 850 } 851 const void* qrt = oat_header.GetQuickResolutionTrampoline(); 852 if (qrt != nullptr) { 853 entry_point_names_[qrt] = "QuickResolutionTrampoline (from boot oat file)"; 854 } 855 const void* qict = oat_header.GetQuickImtConflictTrampoline(); 856 if (qict != nullptr) { 857 entry_point_names_[qict] = "QuickImtConflictTrampoline (from boot oat file)"; 858 } 859 const void* q2ib = oat_header.GetQuickToInterpreterBridge(); 860 if (q2ib != nullptr) { 861 entry_point_names_[q2ib] = "QuickToInterpreterBridge (from boot oat file)"; 862 } 863 } 864 } 865 866 std::string StringFromBytes(const uint8_t* bytes, size_t size) { 867 switch (size) { 868 case 1: 869 return StringPrintf("%" PRIx8, *bytes); 870 case 2: 871 return StringPrintf("%" PRIx16, *reinterpret_cast<const uint16_t*>(bytes)); 872 case 4: 873 case 8: { 874 // Compute an address if the bytes might contain one. 875 uint64_t intval; 876 if (size == 4) { 877 intval = *reinterpret_cast<const uint32_t*>(bytes); 878 } else { 879 intval = *reinterpret_cast<const uint64_t*>(bytes); 880 } 881 const void* addr = reinterpret_cast<const void*>(intval); 882 // Match the address against those that have Is* methods in the ClassLinker. 883 if (class_linker_->IsQuickToInterpreterBridge(addr)) { 884 return "QuickToInterpreterBridge"; 885 } else if (class_linker_->IsQuickGenericJniStub(addr)) { 886 return "QuickGenericJniStub"; 887 } else if (class_linker_->IsQuickResolutionStub(addr)) { 888 return "QuickResolutionStub"; 889 } else if (class_linker_->IsJniDlsymLookupStub(addr)) { 890 return "JniDlsymLookupStub"; 891 } 892 // Match the address against those that we saved from the boot OAT files. 893 if (entry_point_names_.find(addr) != entry_point_names_.end()) { 894 return entry_point_names_[addr]; 895 } 896 return StringPrintf("%" PRIx64, intval); 897 } 898 default: 899 LOG(WARNING) << "Don't know how to convert " << size << " bytes to integer"; 900 return "<UNKNOWN>"; 901 } 902 } 903 904 void DumpOneArtMethod(ArtMethod* art_method, 905 mirror::Class* declaring_class, 906 mirror::Class* remote_declaring_class) 907 REQUIRES_SHARED(Locks::mutator_lock_) { 908 PointerSize pointer_size = InstructionSetPointerSize(Runtime::Current()->GetInstructionSet()); 909 os_ << " " << reinterpret_cast<const void*>(art_method) << " "; 910 os_ << " entryPointFromJni: " 911 << reinterpret_cast<const void*>(art_method->GetDataPtrSize(pointer_size)) << ", "; 912 os_ << " entryPointFromQuickCompiledCode: " 913 << reinterpret_cast<const void*>( 914 art_method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size)) 915 << ", "; 916 os_ << " isNative? " << (art_method->IsNative() ? "yes" : "no") << ", "; 917 // Null for runtime metionds. 918 if (declaring_class != nullptr) { 919 os_ << " class_status (local): " << declaring_class->GetStatus(); 920 } 921 if (remote_declaring_class != nullptr) { 922 os_ << ", class_status (remote): " << remote_declaring_class->GetStatus(); 923 } 924 os_ << "\n"; 925 } 926 927 DISALLOW_COPY_AND_ASSIGN(RegionSpecializedBase); 928 }; 929 930 template <typename T> 931 class RegionData : public RegionSpecializedBase<T> { 932 public: 933 RegionData(std::ostream* os, 934 std::vector<uint8_t>* remote_contents, 935 std::vector<uint8_t>* zygote_contents, 936 const backtrace_map_t& boot_map, 937 const ImageHeader& image_header, 938 bool dump_dirty_objects) 939 : RegionSpecializedBase<T>(os, 940 remote_contents, 941 zygote_contents, 942 boot_map, 943 image_header, 944 dump_dirty_objects), 945 os_(*os) { 946 CHECK(remote_contents != nullptr); 947 CHECK(zygote_contents != nullptr); 948 } 949 950 // Walk over the type T entries in theregion between begin_image_ptr and end_image_ptr, 951 // collecting and reporting data regarding dirty, difference, etc. 952 void ProcessRegion(const MappingData& mapping_data, 953 RemoteProcesses remotes, 954 const uint8_t* begin_image_ptr) 955 REQUIRES_SHARED(Locks::mutator_lock_) { 956 typename RegionSpecializedBase<T>::VisitorClass visitor( 957 [this](T* entry, 958 const uint8_t* begin_image_ptr, 959 const std::set<size_t>& dirty_page_set) REQUIRES_SHARED(Locks::mutator_lock_) { 960 this->ComputeEntryDirty(entry, begin_image_ptr, dirty_page_set); 961 }, 962 begin_image_ptr, 963 mapping_data.dirty_page_set); 964 PointerSize pointer_size = InstructionSetPointerSize(Runtime::Current()->GetInstructionSet()); 965 RegionSpecializedBase<T>::VisitEntries(&visitor, 966 const_cast<uint8_t*>(begin_image_ptr), 967 pointer_size); 968 969 // Looking at only dirty pages, figure out how many of those bytes belong to dirty entries. 970 // TODO: fix this now that there are multiple regions in a mapping. 971 float true_dirtied_percent = 972 RegionCommon<T>::GetDirtyEntryBytes() * 1.0f / (mapping_data.dirty_pages * kPageSize); 973 974 // Entry specific statistics. 975 os_ << RegionCommon<T>::GetDifferentEntryCount() << " different entries, \n " 976 << RegionCommon<T>::GetDirtyEntryBytes() << " different entry [bytes], \n " 977 << RegionCommon<T>::GetFalseDirtyEntryCount() << " false dirty entries,\n " 978 << RegionCommon<T>::GetFalseDirtyEntryBytes() << " false dirty entry [bytes], \n " 979 << true_dirtied_percent << " different entries-vs-total in a dirty page;\n " 980 << "\n"; 981 982 const uint8_t* base_ptr = begin_image_ptr; 983 switch (remotes) { 984 case RemoteProcesses::kZygoteOnly: 985 os_ << " Zygote shared dirty entries: "; 986 break; 987 case RemoteProcesses::kImageAndZygote: 988 os_ << " Application dirty entries (private dirty): "; 989 // If we are dumping private dirty, diff against the zygote map to make it clearer what 990 // fields caused the page to be private dirty. 991 base_ptr = &RegionCommon<T>::zygote_contents_->operator[](0); 992 break; 993 case RemoteProcesses::kImageOnly: 994 os_ << " Application dirty entries (unknown whether private or shared dirty): "; 995 break; 996 } 997 DiffDirtyEntries(ProcessType::kRemote, 998 begin_image_ptr, 999 RegionCommon<T>::remote_contents_, 1000 base_ptr, 1001 /*log_dirty_objects*/true); 1002 // Print shared dirty after since it's less important. 1003 if (RegionCommon<T>::GetZygoteDirtyEntryCount() != 0) { 1004 // We only reach this point if both pids were specified. Furthermore, 1005 // entries are only displayed here if they differed in both the image 1006 // and the zygote, so they are probably private dirty. 1007 CHECK(remotes == RemoteProcesses::kImageAndZygote); 1008 os_ << "\n" << " Zygote dirty entries (probably shared dirty): "; 1009 DiffDirtyEntries(ProcessType::kZygote, 1010 begin_image_ptr, 1011 RegionCommon<T>::zygote_contents_, 1012 begin_image_ptr, 1013 /*log_dirty_objects*/false); 1014 } 1015 RegionSpecializedBase<T>::DumpDirtyObjects(); 1016 RegionSpecializedBase<T>::DumpDirtyEntries(); 1017 RegionSpecializedBase<T>::DumpFalseDirtyEntries(); 1018 RegionSpecializedBase<T>::DumpCleanEntries(); 1019 } 1020 1021 private: 1022 std::ostream& os_; 1023 1024 void DiffDirtyEntries(ProcessType process_type, 1025 const uint8_t* begin_image_ptr, 1026 std::vector<uint8_t>* contents, 1027 const uint8_t* base_ptr, 1028 bool log_dirty_objects) 1029 REQUIRES_SHARED(Locks::mutator_lock_) { 1030 os_ << RegionCommon<T>::dirty_entries_.size() << "\n"; 1031 const std::set<T*>& entries = 1032 (process_type == ProcessType::kZygote) ? 1033 RegionCommon<T>::zygote_dirty_entries_: 1034 RegionCommon<T>::image_dirty_entries_; 1035 for (T* entry : entries) { 1036 uint8_t* entry_bytes = reinterpret_cast<uint8_t*>(entry); 1037 ptrdiff_t offset = entry_bytes - begin_image_ptr; 1038 uint8_t* remote_bytes = &(*contents)[offset]; 1039 RegionSpecializedBase<T>::DiffEntryContents(entry, 1040 remote_bytes, 1041 &base_ptr[offset], 1042 log_dirty_objects); 1043 } 1044 } 1045 1046 void ComputeEntryDirty(T* entry, 1047 const uint8_t* begin_image_ptr, 1048 const std::set<size_t>& dirty_pages) 1049 REQUIRES_SHARED(Locks::mutator_lock_) { 1050 // Set up pointers in the remote and the zygote for comparison. 1051 uint8_t* current = reinterpret_cast<uint8_t*>(entry); 1052 ptrdiff_t offset = current - begin_image_ptr; 1053 T* entry_remote = 1054 reinterpret_cast<T*>(const_cast<uint8_t*>(&(*RegionCommon<T>::remote_contents_)[offset])); 1055 const bool have_zygote = !RegionCommon<T>::zygote_contents_->empty(); 1056 const uint8_t* current_zygote = 1057 have_zygote ? &(*RegionCommon<T>::zygote_contents_)[offset] : nullptr; 1058 T* entry_zygote = reinterpret_cast<T*>(const_cast<uint8_t*>(current_zygote)); 1059 // Visit and classify entries at the current location. 1060 RegionSpecializedBase<T>::VisitEntry(entry); 1061 1062 // Test private dirty first. 1063 bool is_dirty = false; 1064 if (have_zygote) { 1065 bool private_dirty = EntriesDiffer(entry_zygote, entry_remote); 1066 if (private_dirty) { 1067 // Private dirty, app vs zygote. 1068 is_dirty = true; 1069 RegionCommon<T>::AddImageDirtyEntry(entry); 1070 } 1071 if (EntriesDiffer(entry_zygote, entry)) { 1072 // Shared dirty, zygote vs image. 1073 is_dirty = true; 1074 RegionCommon<T>::AddZygoteDirtyEntry(entry); 1075 } 1076 } else if (EntriesDiffer(entry_remote, entry)) { 1077 // Shared or private dirty, app vs image. 1078 is_dirty = true; 1079 RegionCommon<T>::AddImageDirtyEntry(entry); 1080 } 1081 if (is_dirty) { 1082 // TODO: Add support dirty entries in zygote and image. 1083 RegionSpecializedBase<T>::AddDirtyEntry(entry, entry_remote); 1084 } else { 1085 RegionSpecializedBase<T>::AddCleanEntry(entry); 1086 if (RegionCommon<T>::IsEntryOnDirtyPage(entry, dirty_pages)) { 1087 // This entry was either never mutated or got mutated back to the same value. 1088 // TODO: Do I want to distinguish a "different" vs a "dirty" page here? 1089 RegionSpecializedBase<T>::AddFalseDirtyEntry(entry); 1090 } 1091 } 1092 } 1093 1094 DISALLOW_COPY_AND_ASSIGN(RegionData); 1095 }; 1096 1097 } // namespace 1098 1099 1100 class ImgDiagDumper { 1101 public: 1102 explicit ImgDiagDumper(std::ostream* os, 1103 const ImageHeader& image_header, 1104 const std::string& image_location, 1105 pid_t image_diff_pid, 1106 pid_t zygote_diff_pid, 1107 bool dump_dirty_objects) 1108 : os_(os), 1109 image_header_(image_header), 1110 image_location_(image_location), 1111 image_diff_pid_(image_diff_pid), 1112 zygote_diff_pid_(zygote_diff_pid), 1113 dump_dirty_objects_(dump_dirty_objects), 1114 zygote_pid_only_(false) {} 1115 1116 bool Init() { 1117 std::ostream& os = *os_; 1118 1119 if (image_diff_pid_ < 0 && zygote_diff_pid_ < 0) { 1120 os << "Either --image-diff-pid or --zygote-diff-pid (or both) must be specified.\n"; 1121 return false; 1122 } 1123 1124 // To avoid the combinations of command-line argument use cases: 1125 // If the user invoked with only --zygote-diff-pid, shuffle that to 1126 // image_diff_pid_, invalidate zygote_diff_pid_, and remember that 1127 // image_diff_pid_ is now special. 1128 if (image_diff_pid_ < 0) { 1129 image_diff_pid_ = zygote_diff_pid_; 1130 zygote_diff_pid_ = -1; 1131 zygote_pid_only_ = true; 1132 } 1133 1134 { 1135 struct stat sts; 1136 std::string proc_pid_str = 1137 StringPrintf("/proc/%ld", static_cast<long>(image_diff_pid_)); // NOLINT [runtime/int] 1138 if (stat(proc_pid_str.c_str(), &sts) == -1) { 1139 os << "Process does not exist"; 1140 return false; 1141 } 1142 } 1143 1144 // Open /proc/$pid/maps to view memory maps 1145 auto tmp_proc_maps = std::unique_ptr<BacktraceMap>(BacktraceMap::Create(image_diff_pid_)); 1146 if (tmp_proc_maps == nullptr) { 1147 os << "Could not read backtrace maps"; 1148 return false; 1149 } 1150 1151 bool found_boot_map = false; 1152 // Find the memory map only for boot.art 1153 for (const backtrace_map_t& map : *tmp_proc_maps) { 1154 if (EndsWith(map.name, GetImageLocationBaseName())) { 1155 if ((map.flags & PROT_WRITE) != 0) { 1156 boot_map_ = map; 1157 found_boot_map = true; 1158 break; 1159 } 1160 // In actuality there's more than 1 map, but the second one is read-only. 1161 // The one we care about is the write-able map. 1162 // The readonly maps are guaranteed to be identical, so its not interesting to compare 1163 // them. 1164 } 1165 } 1166 1167 if (!found_boot_map) { 1168 os << "Could not find map for " << GetImageLocationBaseName(); 1169 return false; 1170 } 1171 // Sanity check boot_map_. 1172 CHECK(boot_map_.end >= boot_map_.start); 1173 boot_map_size_ = boot_map_.end - boot_map_.start; 1174 1175 // Open /proc/<image_diff_pid_>/mem and read as remote_contents_. 1176 std::string image_file_name = 1177 StringPrintf("/proc/%ld/mem", static_cast<long>(image_diff_pid_)); // NOLINT [runtime/int] 1178 auto image_map_file = std::unique_ptr<File>(OS::OpenFileForReading(image_file_name.c_str())); 1179 if (image_map_file == nullptr) { 1180 os << "Failed to open " << image_file_name << " for reading"; 1181 return false; 1182 } 1183 std::vector<uint8_t> tmp_remote_contents(boot_map_size_); 1184 if (!image_map_file->PreadFully(&tmp_remote_contents[0], boot_map_size_, boot_map_.start)) { 1185 os << "Could not fully read file " << image_file_name; 1186 return false; 1187 } 1188 1189 // If zygote_diff_pid_ != -1, open /proc/<zygote_diff_pid_>/mem and read as zygote_contents_. 1190 std::vector<uint8_t> tmp_zygote_contents; 1191 if (zygote_diff_pid_ != -1) { 1192 std::string zygote_file_name = 1193 StringPrintf("/proc/%ld/mem", static_cast<long>(zygote_diff_pid_)); // NOLINT [runtime/int] 1194 std::unique_ptr<File> zygote_map_file(OS::OpenFileForReading(zygote_file_name.c_str())); 1195 if (zygote_map_file == nullptr) { 1196 os << "Failed to open " << zygote_file_name << " for reading"; 1197 return false; 1198 } 1199 // The boot map should be at the same address. 1200 tmp_zygote_contents.resize(boot_map_size_); 1201 if (!zygote_map_file->PreadFully(&tmp_zygote_contents[0], boot_map_size_, boot_map_.start)) { 1202 LOG(WARNING) << "Could not fully read zygote file " << zygote_file_name; 1203 return false; 1204 } 1205 } 1206 1207 // Open /proc/<image_diff_pid_>/pagemap. 1208 std::string pagemap_file_name = StringPrintf( 1209 "/proc/%ld/pagemap", static_cast<long>(image_diff_pid_)); // NOLINT [runtime/int] 1210 auto tmp_pagemap_file = 1211 std::unique_ptr<File>(OS::OpenFileForReading(pagemap_file_name.c_str())); 1212 if (tmp_pagemap_file == nullptr) { 1213 os << "Failed to open " << pagemap_file_name << " for reading: " << strerror(errno); 1214 return false; 1215 } 1216 1217 // Not truly clean, mmap-ing boot.art again would be more pristine, but close enough 1218 const char* clean_pagemap_file_name = "/proc/self/pagemap"; 1219 auto tmp_clean_pagemap_file = std::unique_ptr<File>( 1220 OS::OpenFileForReading(clean_pagemap_file_name)); 1221 if (tmp_clean_pagemap_file == nullptr) { 1222 os << "Failed to open " << clean_pagemap_file_name << " for reading: " << strerror(errno); 1223 return false; 1224 } 1225 1226 auto tmp_kpageflags_file = std::unique_ptr<File>(OS::OpenFileForReading("/proc/kpageflags")); 1227 if (tmp_kpageflags_file == nullptr) { 1228 os << "Failed to open /proc/kpageflags for reading: " << strerror(errno); 1229 return false; 1230 } 1231 1232 auto tmp_kpagecount_file = std::unique_ptr<File>(OS::OpenFileForReading("/proc/kpagecount")); 1233 if (tmp_kpagecount_file == nullptr) { 1234 os << "Failed to open /proc/kpagecount for reading:" << strerror(errno); 1235 return false; 1236 } 1237 1238 // Commit the mappings, etc. 1239 proc_maps_ = std::move(tmp_proc_maps); 1240 remote_contents_ = std::move(tmp_remote_contents); 1241 zygote_contents_ = std::move(tmp_zygote_contents); 1242 pagemap_file_ = std::move(*tmp_pagemap_file.release()); 1243 clean_pagemap_file_ = std::move(*tmp_clean_pagemap_file.release()); 1244 kpageflags_file_ = std::move(*tmp_kpageflags_file.release()); 1245 kpagecount_file_ = std::move(*tmp_kpagecount_file.release()); 1246 1247 return true; 1248 } 1249 1250 bool Dump() REQUIRES_SHARED(Locks::mutator_lock_) { 1251 std::ostream& os = *os_; 1252 os << "IMAGE LOCATION: " << image_location_ << "\n\n"; 1253 1254 os << "MAGIC: " << image_header_.GetMagic() << "\n\n"; 1255 1256 os << "IMAGE BEGIN: " << reinterpret_cast<void*>(image_header_.GetImageBegin()) << "\n\n"; 1257 1258 PrintPidLine("IMAGE", image_diff_pid_); 1259 os << "\n\n"; 1260 PrintPidLine("ZYGOTE", zygote_diff_pid_); 1261 bool ret = true; 1262 if (image_diff_pid_ >= 0 || zygote_diff_pid_ >= 0) { 1263 ret = DumpImageDiff(); 1264 os << "\n\n"; 1265 } 1266 1267 os << std::flush; 1268 1269 return ret; 1270 } 1271 1272 private: 1273 bool DumpImageDiff() 1274 REQUIRES_SHARED(Locks::mutator_lock_) { 1275 return DumpImageDiffMap(); 1276 } 1277 1278 bool ComputeDirtyBytes(const uint8_t* image_begin, MappingData* mapping_data /*out*/) { 1279 std::ostream& os = *os_; 1280 1281 size_t virtual_page_idx = 0; // Virtual page number (for an absolute memory address) 1282 size_t page_idx = 0; // Page index relative to 0 1283 size_t previous_page_idx = 0; // Previous page index relative to 0 1284 1285 1286 // Iterate through one page at a time. Boot map begin/end already implicitly aligned. 1287 for (uintptr_t begin = boot_map_.start; begin != boot_map_.end; begin += kPageSize) { 1288 ptrdiff_t offset = begin - boot_map_.start; 1289 1290 // We treat the image header as part of the memory map for now 1291 // If we wanted to change this, we could pass base=start+sizeof(ImageHeader) 1292 // But it might still be interesting to see if any of the ImageHeader data mutated 1293 const uint8_t* local_ptr = reinterpret_cast<const uint8_t*>(&image_header_) + offset; 1294 uint8_t* remote_ptr = &remote_contents_[offset]; 1295 1296 if (memcmp(local_ptr, remote_ptr, kPageSize) != 0) { 1297 mapping_data->different_pages++; 1298 1299 // Count the number of 32-bit integers that are different. 1300 for (size_t i = 0; i < kPageSize / sizeof(uint32_t); ++i) { 1301 uint32_t* remote_ptr_int32 = reinterpret_cast<uint32_t*>(remote_ptr); 1302 const uint32_t* local_ptr_int32 = reinterpret_cast<const uint32_t*>(local_ptr); 1303 1304 if (remote_ptr_int32[i] != local_ptr_int32[i]) { 1305 mapping_data->different_int32s++; 1306 } 1307 } 1308 } 1309 } 1310 1311 std::vector<size_t> private_dirty_pages_for_section(ImageHeader::kSectionCount, 0u); 1312 1313 // Iterate through one byte at a time. 1314 ptrdiff_t page_off_begin = image_header_.GetImageBegin() - image_begin; 1315 for (uintptr_t begin = boot_map_.start; begin != boot_map_.end; ++begin) { 1316 previous_page_idx = page_idx; 1317 ptrdiff_t offset = begin - boot_map_.start; 1318 1319 // We treat the image header as part of the memory map for now 1320 // If we wanted to change this, we could pass base=start+sizeof(ImageHeader) 1321 // But it might still be interesting to see if any of the ImageHeader data mutated 1322 const uint8_t* local_ptr = reinterpret_cast<const uint8_t*>(&image_header_) + offset; 1323 uint8_t* remote_ptr = &remote_contents_[offset]; 1324 1325 virtual_page_idx = reinterpret_cast<uintptr_t>(local_ptr) / kPageSize; 1326 1327 // Calculate the page index, relative to the 0th page where the image begins 1328 page_idx = (offset + page_off_begin) / kPageSize; 1329 if (*local_ptr != *remote_ptr) { 1330 // Track number of bytes that are different 1331 mapping_data->different_bytes++; 1332 } 1333 1334 // Independently count the # of dirty pages on the remote side 1335 size_t remote_virtual_page_idx = begin / kPageSize; 1336 if (previous_page_idx != page_idx) { 1337 uint64_t page_count = 0xC0FFEE; 1338 // TODO: virtual_page_idx needs to be from the same process 1339 std::string error_msg; 1340 int dirtiness = (IsPageDirty(&pagemap_file_, // Image-diff-pid procmap 1341 &clean_pagemap_file_, // Self procmap 1342 &kpageflags_file_, 1343 &kpagecount_file_, 1344 remote_virtual_page_idx, // potentially "dirty" page 1345 virtual_page_idx, // true "clean" page 1346 &page_count, 1347 &error_msg)); 1348 if (dirtiness < 0) { 1349 os << error_msg; 1350 return false; 1351 } else if (dirtiness > 0) { 1352 mapping_data->dirty_pages++; 1353 mapping_data->dirty_page_set.insert(mapping_data->dirty_page_set.end(), virtual_page_idx); 1354 } 1355 1356 bool is_dirty = dirtiness > 0; 1357 bool is_private = page_count == 1; 1358 1359 if (page_count == 1) { 1360 mapping_data->private_pages++; 1361 } 1362 1363 if (is_dirty && is_private) { 1364 mapping_data->private_dirty_pages++; 1365 for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) { 1366 const ImageHeader::ImageSections section = static_cast<ImageHeader::ImageSections>(i); 1367 if (image_header_.GetImageSection(section).Contains(offset)) { 1368 ++private_dirty_pages_for_section[i]; 1369 } 1370 } 1371 } 1372 } 1373 } 1374 mapping_data->false_dirty_pages = mapping_data->dirty_pages - mapping_data->different_pages; 1375 // Print low-level (bytes, int32s, pages) statistics. 1376 os << mapping_data->different_bytes << " differing bytes,\n " 1377 << mapping_data->different_int32s << " differing int32s,\n " 1378 << mapping_data->different_pages << " differing pages,\n " 1379 << mapping_data->dirty_pages << " pages are dirty;\n " 1380 << mapping_data->false_dirty_pages << " pages are false dirty;\n " 1381 << mapping_data->private_pages << " pages are private;\n " 1382 << mapping_data->private_dirty_pages << " pages are Private_Dirty\n " 1383 << "\n"; 1384 1385 size_t total_private_dirty_pages = std::accumulate(private_dirty_pages_for_section.begin(), 1386 private_dirty_pages_for_section.end(), 1387 0u); 1388 os << "Image sections (total private dirty pages " << total_private_dirty_pages << ")\n"; 1389 for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) { 1390 const ImageHeader::ImageSections section = static_cast<ImageHeader::ImageSections>(i); 1391 os << section << " " << image_header_.GetImageSection(section) 1392 << " private dirty pages=" << private_dirty_pages_for_section[i] << "\n"; 1393 } 1394 os << "\n"; 1395 1396 return true; 1397 } 1398 1399 // Look at /proc/$pid/mem and only diff the things from there 1400 bool DumpImageDiffMap() 1401 REQUIRES_SHARED(Locks::mutator_lock_) { 1402 std::ostream& os = *os_; 1403 std::string error_msg; 1404 1405 // Walk the bytes and diff against our boot image 1406 os << "\nObserving boot image header at address " 1407 << reinterpret_cast<const void*>(&image_header_) 1408 << "\n\n"; 1409 1410 const uint8_t* image_begin_unaligned = image_header_.GetImageBegin(); 1411 const uint8_t* image_end_unaligned = image_begin_unaligned + image_header_.GetImageSize(); 1412 1413 // Adjust range to nearest page 1414 const uint8_t* image_begin = AlignDown(image_begin_unaligned, kPageSize); 1415 const uint8_t* image_end = AlignUp(image_end_unaligned, kPageSize); 1416 1417 if (reinterpret_cast<uintptr_t>(image_begin) > boot_map_.start || 1418 reinterpret_cast<uintptr_t>(image_end) < boot_map_.end) { 1419 // Sanity check that we aren't trying to read a completely different boot image 1420 os << "Remote boot map is out of range of local boot map: " << 1421 "local begin " << reinterpret_cast<const void*>(image_begin) << 1422 ", local end " << reinterpret_cast<const void*>(image_end) << 1423 ", remote begin " << reinterpret_cast<const void*>(boot_map_.start) << 1424 ", remote end " << reinterpret_cast<const void*>(boot_map_.end); 1425 return false; 1426 // If we wanted even more validation we could map the ImageHeader from the file 1427 } 1428 1429 MappingData mapping_data; 1430 1431 os << "Mapping at [" << reinterpret_cast<void*>(boot_map_.start) << ", " 1432 << reinterpret_cast<void*>(boot_map_.end) << ") had:\n "; 1433 if (!ComputeDirtyBytes(image_begin, &mapping_data)) { 1434 return false; 1435 } 1436 RemoteProcesses remotes; 1437 if (zygote_pid_only_) { 1438 remotes = RemoteProcesses::kZygoteOnly; 1439 } else if (zygote_diff_pid_ > 0) { 1440 remotes = RemoteProcesses::kImageAndZygote; 1441 } else { 1442 remotes = RemoteProcesses::kImageOnly; 1443 } 1444 1445 // Check all the mirror::Object entries in the image. 1446 RegionData<mirror::Object> object_region_data(os_, 1447 &remote_contents_, 1448 &zygote_contents_, 1449 boot_map_, 1450 image_header_, 1451 dump_dirty_objects_); 1452 object_region_data.ProcessRegion(mapping_data, 1453 remotes, 1454 image_begin_unaligned); 1455 1456 // Check all the ArtMethod entries in the image. 1457 RegionData<ArtMethod> artmethod_region_data(os_, 1458 &remote_contents_, 1459 &zygote_contents_, 1460 boot_map_, 1461 image_header_, 1462 dump_dirty_objects_); 1463 artmethod_region_data.ProcessRegion(mapping_data, 1464 remotes, 1465 image_begin_unaligned); 1466 return true; 1467 } 1468 1469 static bool GetPageFrameNumber(File* page_map_file, 1470 size_t virtual_page_index, 1471 uint64_t* page_frame_number, 1472 std::string* error_msg) { 1473 CHECK(page_map_file != nullptr); 1474 CHECK(page_frame_number != nullptr); 1475 CHECK(error_msg != nullptr); 1476 1477 constexpr size_t kPageMapEntrySize = sizeof(uint64_t); 1478 constexpr uint64_t kPageFrameNumberMask = (1ULL << 55) - 1; // bits 0-54 [in /proc/$pid/pagemap] 1479 constexpr uint64_t kPageSoftDirtyMask = (1ULL << 55); // bit 55 [in /proc/$pid/pagemap] 1480 1481 uint64_t page_map_entry = 0; 1482 1483 // Read 64-bit entry from /proc/$pid/pagemap to get the physical page frame number 1484 if (!page_map_file->PreadFully(&page_map_entry, kPageMapEntrySize, 1485 virtual_page_index * kPageMapEntrySize)) { 1486 *error_msg = StringPrintf("Failed to read the virtual page index entry from %s", 1487 page_map_file->GetPath().c_str()); 1488 return false; 1489 } 1490 1491 // TODO: seems useless, remove this. 1492 bool soft_dirty = (page_map_entry & kPageSoftDirtyMask) != 0; 1493 if ((false)) { 1494 LOG(VERBOSE) << soft_dirty; // Suppress unused warning 1495 UNREACHABLE(); 1496 } 1497 1498 *page_frame_number = page_map_entry & kPageFrameNumberMask; 1499 1500 return true; 1501 } 1502 1503 static int IsPageDirty(File* page_map_file, 1504 File* clean_pagemap_file, 1505 File* kpageflags_file, 1506 File* kpagecount_file, 1507 size_t virtual_page_idx, 1508 size_t clean_virtual_page_idx, 1509 // Out parameters: 1510 uint64_t* page_count, std::string* error_msg) { 1511 CHECK(page_map_file != nullptr); 1512 CHECK(clean_pagemap_file != nullptr); 1513 CHECK_NE(page_map_file, clean_pagemap_file); 1514 CHECK(kpageflags_file != nullptr); 1515 CHECK(kpagecount_file != nullptr); 1516 CHECK(page_count != nullptr); 1517 CHECK(error_msg != nullptr); 1518 1519 // Constants are from https://www.kernel.org/doc/Documentation/vm/pagemap.txt 1520 1521 constexpr size_t kPageFlagsEntrySize = sizeof(uint64_t); 1522 constexpr size_t kPageCountEntrySize = sizeof(uint64_t); 1523 constexpr uint64_t kPageFlagsDirtyMask = (1ULL << 4); // in /proc/kpageflags 1524 constexpr uint64_t kPageFlagsNoPageMask = (1ULL << 20); // in /proc/kpageflags 1525 constexpr uint64_t kPageFlagsMmapMask = (1ULL << 11); // in /proc/kpageflags 1526 1527 uint64_t page_frame_number = 0; 1528 if (!GetPageFrameNumber(page_map_file, virtual_page_idx, &page_frame_number, error_msg)) { 1529 return -1; 1530 } 1531 1532 uint64_t page_frame_number_clean = 0; 1533 if (!GetPageFrameNumber(clean_pagemap_file, clean_virtual_page_idx, &page_frame_number_clean, 1534 error_msg)) { 1535 return -1; 1536 } 1537 1538 // Read 64-bit entry from /proc/kpageflags to get the dirty bit for a page 1539 uint64_t kpage_flags_entry = 0; 1540 if (!kpageflags_file->PreadFully(&kpage_flags_entry, 1541 kPageFlagsEntrySize, 1542 page_frame_number * kPageFlagsEntrySize)) { 1543 *error_msg = StringPrintf("Failed to read the page flags from %s", 1544 kpageflags_file->GetPath().c_str()); 1545 return -1; 1546 } 1547 1548 // Read 64-bit entyry from /proc/kpagecount to get mapping counts for a page 1549 if (!kpagecount_file->PreadFully(page_count /*out*/, 1550 kPageCountEntrySize, 1551 page_frame_number * kPageCountEntrySize)) { 1552 *error_msg = StringPrintf("Failed to read the page count from %s", 1553 kpagecount_file->GetPath().c_str()); 1554 return -1; 1555 } 1556 1557 // There must be a page frame at the requested address. 1558 CHECK_EQ(kpage_flags_entry & kPageFlagsNoPageMask, 0u); 1559 // The page frame must be memory mapped 1560 CHECK_NE(kpage_flags_entry & kPageFlagsMmapMask, 0u); 1561 1562 // Page is dirty, i.e. has diverged from file, if the 4th bit is set to 1 1563 bool flags_dirty = (kpage_flags_entry & kPageFlagsDirtyMask) != 0; 1564 1565 // page_frame_number_clean must come from the *same* process 1566 // but a *different* mmap than page_frame_number 1567 if (flags_dirty) { 1568 CHECK_NE(page_frame_number, page_frame_number_clean); 1569 } 1570 1571 return page_frame_number != page_frame_number_clean; 1572 } 1573 1574 void PrintPidLine(const std::string& kind, pid_t pid) { 1575 if (pid < 0) { 1576 *os_ << kind << " DIFF PID: disabled\n\n"; 1577 } else { 1578 *os_ << kind << " DIFF PID (" << pid << "): "; 1579 } 1580 } 1581 1582 static bool EndsWith(const std::string& str, const std::string& suffix) { 1583 return str.size() >= suffix.size() && 1584 str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; 1585 } 1586 1587 // Return suffix of the file path after the last /. (e.g. /foo/bar -> bar, bar -> bar) 1588 static std::string BaseName(const std::string& str) { 1589 size_t idx = str.rfind('/'); 1590 if (idx == std::string::npos) { 1591 return str; 1592 } 1593 1594 return str.substr(idx + 1); 1595 } 1596 1597 // Return the image location, stripped of any directories, e.g. "boot.art" or "core.art" 1598 std::string GetImageLocationBaseName() const { 1599 return BaseName(std::string(image_location_)); 1600 } 1601 1602 std::ostream* os_; 1603 const ImageHeader& image_header_; 1604 const std::string image_location_; 1605 pid_t image_diff_pid_; // Dump image diff against boot.art if pid is non-negative 1606 pid_t zygote_diff_pid_; // Dump image diff against zygote boot.art if pid is non-negative 1607 bool dump_dirty_objects_; // Adds dumping of objects that are dirty. 1608 bool zygote_pid_only_; // The user only specified a pid for the zygote. 1609 1610 // BacktraceMap used for finding the memory mapping of the image file. 1611 std::unique_ptr<BacktraceMap> proc_maps_; 1612 // Boot image mapping. 1613 backtrace_map_t boot_map_{}; // NOLINT 1614 // The size of the boot image mapping. 1615 size_t boot_map_size_; 1616 // The contents of /proc/<image_diff_pid_>/maps. 1617 std::vector<uint8_t> remote_contents_; 1618 // The contents of /proc/<zygote_diff_pid_>/maps. 1619 std::vector<uint8_t> zygote_contents_; 1620 // A File for reading /proc/<zygote_diff_pid_>/maps. 1621 File pagemap_file_; 1622 // A File for reading /proc/self/pagemap. 1623 File clean_pagemap_file_; 1624 // A File for reading /proc/kpageflags. 1625 File kpageflags_file_; 1626 // A File for reading /proc/kpagecount. 1627 File kpagecount_file_; 1628 1629 DISALLOW_COPY_AND_ASSIGN(ImgDiagDumper); 1630 }; 1631 1632 static int DumpImage(Runtime* runtime, 1633 std::ostream* os, 1634 pid_t image_diff_pid, 1635 pid_t zygote_diff_pid, 1636 bool dump_dirty_objects) { 1637 ScopedObjectAccess soa(Thread::Current()); 1638 gc::Heap* heap = runtime->GetHeap(); 1639 std::vector<gc::space::ImageSpace*> image_spaces = heap->GetBootImageSpaces(); 1640 CHECK(!image_spaces.empty()); 1641 for (gc::space::ImageSpace* image_space : image_spaces) { 1642 const ImageHeader& image_header = image_space->GetImageHeader(); 1643 if (!image_header.IsValid()) { 1644 fprintf(stderr, "Invalid image header %s\n", image_space->GetImageLocation().c_str()); 1645 return EXIT_FAILURE; 1646 } 1647 1648 ImgDiagDumper img_diag_dumper(os, 1649 image_header, 1650 image_space->GetImageLocation(), 1651 image_diff_pid, 1652 zygote_diff_pid, 1653 dump_dirty_objects); 1654 if (!img_diag_dumper.Init()) { 1655 return EXIT_FAILURE; 1656 } 1657 if (!img_diag_dumper.Dump()) { 1658 return EXIT_FAILURE; 1659 } 1660 } 1661 return EXIT_SUCCESS; 1662 } 1663 1664 struct ImgDiagArgs : public CmdlineArgs { 1665 protected: 1666 using Base = CmdlineArgs; 1667 1668 virtual ParseStatus ParseCustom(const StringPiece& option, 1669 std::string* error_msg) OVERRIDE { 1670 { 1671 ParseStatus base_parse = Base::ParseCustom(option, error_msg); 1672 if (base_parse != kParseUnknownArgument) { 1673 return base_parse; 1674 } 1675 } 1676 1677 if (option.starts_with("--image-diff-pid=")) { 1678 const char* image_diff_pid = option.substr(strlen("--image-diff-pid=")).data(); 1679 1680 if (!ParseInt(image_diff_pid, &image_diff_pid_)) { 1681 *error_msg = "Image diff pid out of range"; 1682 return kParseError; 1683 } 1684 } else if (option.starts_with("--zygote-diff-pid=")) { 1685 const char* zygote_diff_pid = option.substr(strlen("--zygote-diff-pid=")).data(); 1686 1687 if (!ParseInt(zygote_diff_pid, &zygote_diff_pid_)) { 1688 *error_msg = "Zygote diff pid out of range"; 1689 return kParseError; 1690 } 1691 } else if (option == "--dump-dirty-objects") { 1692 dump_dirty_objects_ = true; 1693 } else { 1694 return kParseUnknownArgument; 1695 } 1696 1697 return kParseOk; 1698 } 1699 1700 virtual ParseStatus ParseChecks(std::string* error_msg) OVERRIDE { 1701 // Perform the parent checks. 1702 ParseStatus parent_checks = Base::ParseChecks(error_msg); 1703 if (parent_checks != kParseOk) { 1704 return parent_checks; 1705 } 1706 1707 // Perform our own checks. 1708 1709 if (kill(image_diff_pid_, 1710 /*sig*/0) != 0) { // No signal is sent, perform error-checking only. 1711 // Check if the pid exists before proceeding. 1712 if (errno == ESRCH) { 1713 *error_msg = "Process specified does not exist"; 1714 } else { 1715 *error_msg = StringPrintf("Failed to check process status: %s", strerror(errno)); 1716 } 1717 return kParseError; 1718 } else if (instruction_set_ != kRuntimeISA) { 1719 // Don't allow different ISAs since the images are ISA-specific. 1720 // Right now the code assumes both the runtime ISA and the remote ISA are identical. 1721 *error_msg = "Must use the default runtime ISA; changing ISA is not supported."; 1722 return kParseError; 1723 } 1724 1725 return kParseOk; 1726 } 1727 1728 virtual std::string GetUsage() const { 1729 std::string usage; 1730 1731 usage += 1732 "Usage: imgdiag [options] ...\n" 1733 " Example: imgdiag --image-diff-pid=$(pidof dex2oat)\n" 1734 " Example: adb shell imgdiag --image-diff-pid=$(pid zygote)\n" 1735 "\n"; 1736 1737 usage += Base::GetUsage(); 1738 1739 usage += // Optional. 1740 " --image-diff-pid=<pid>: provide the PID of a process whose boot.art you want to diff.\n" 1741 " Example: --image-diff-pid=$(pid zygote)\n" 1742 " --zygote-diff-pid=<pid>: provide the PID of the zygote whose boot.art you want to diff " 1743 "against.\n" 1744 " Example: --zygote-diff-pid=$(pid zygote)\n" 1745 " --dump-dirty-objects: additionally output dirty objects of interest.\n" 1746 "\n"; 1747 1748 return usage; 1749 } 1750 1751 public: 1752 pid_t image_diff_pid_ = -1; 1753 pid_t zygote_diff_pid_ = -1; 1754 bool dump_dirty_objects_ = false; 1755 }; 1756 1757 struct ImgDiagMain : public CmdlineMain<ImgDiagArgs> { 1758 virtual bool ExecuteWithRuntime(Runtime* runtime) { 1759 CHECK(args_ != nullptr); 1760 1761 return DumpImage(runtime, 1762 args_->os_, 1763 args_->image_diff_pid_, 1764 args_->zygote_diff_pid_, 1765 args_->dump_dirty_objects_) == EXIT_SUCCESS; 1766 } 1767 }; 1768 1769 } // namespace art 1770 1771 int main(int argc, char** argv) { 1772 art::ImgDiagMain main; 1773 return main.Main(argc, argv); 1774 } 1775