1 /* 2 * Copyright (C) 2017 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 <errno.h> 18 #include <inttypes.h> 19 #include <stdint.h> 20 #include <stdlib.h> 21 #include <string.h> 22 23 #include <iostream> 24 #include <memory> 25 #include <vector> 26 27 #include "android-base/stringprintf.h" 28 29 #include "base/stringpiece.h" 30 31 #include "dex_file.h" 32 #include "dex_ir.h" 33 #include "dex_ir_builder.h" 34 #ifdef ART_TARGET_ANDROID 35 #include "pagemap/pagemap.h" 36 #endif 37 #include "runtime.h" 38 #include "vdex_file.h" 39 40 namespace art { 41 42 using android::base::StringPrintf; 43 44 static bool g_verbose = false; 45 46 // The width needed to print a file page offset (32-bit). 47 static constexpr int kPageCountWidth = 48 static_cast<int>(std::numeric_limits<uint32_t>::digits10); 49 // Display the sections. 50 static constexpr char kSectionHeader[] = "Section name"; 51 52 struct DexSectionInfo { 53 public: 54 std::string name; 55 char letter; 56 }; 57 58 static const std::map<uint16_t, DexSectionInfo> kDexSectionInfoMap = { 59 { DexFile::kDexTypeHeaderItem, { "Header", 'H' } }, 60 { DexFile::kDexTypeStringIdItem, { "StringId", 'S' } }, 61 { DexFile::kDexTypeTypeIdItem, { "TypeId", 'T' } }, 62 { DexFile::kDexTypeProtoIdItem, { "ProtoId", 'P' } }, 63 { DexFile::kDexTypeFieldIdItem, { "FieldId", 'F' } }, 64 { DexFile::kDexTypeMethodIdItem, { "MethodId", 'M' } }, 65 { DexFile::kDexTypeClassDefItem, { "ClassDef", 'C' } }, 66 { DexFile::kDexTypeCallSiteIdItem, { "CallSiteId", 'z' } }, 67 { DexFile::kDexTypeMethodHandleItem, { "MethodHandle", 'Z' } }, 68 { DexFile::kDexTypeMapList, { "TypeMap", 'L' } }, 69 { DexFile::kDexTypeTypeList, { "TypeList", 't' } }, 70 { DexFile::kDexTypeAnnotationSetRefList, { "AnnotationSetReferenceItem", '1' } }, 71 { DexFile::kDexTypeAnnotationSetItem, { "AnnotationSetItem", '2' } }, 72 { DexFile::kDexTypeClassDataItem, { "ClassData", 'c' } }, 73 { DexFile::kDexTypeCodeItem, { "CodeItem", 'X' } }, 74 { DexFile::kDexTypeStringDataItem, { "StringData", 's' } }, 75 { DexFile::kDexTypeDebugInfoItem, { "DebugInfo", 'D' } }, 76 { DexFile::kDexTypeAnnotationItem, { "AnnotationItem", '3' } }, 77 { DexFile::kDexTypeEncodedArrayItem, { "EncodedArrayItem", 'E' } }, 78 { DexFile::kDexTypeAnnotationsDirectoryItem, { "AnnotationsDirectoryItem", '4' } } 79 }; 80 81 class PageCount { 82 public: 83 PageCount() { 84 for (auto it = kDexSectionInfoMap.begin(); it != kDexSectionInfoMap.end(); ++it) { 85 map_[it->first] = 0; 86 } 87 } 88 void Increment(uint16_t type) { 89 map_[type]++; 90 } 91 size_t Get(uint16_t type) const { 92 return map_.at(type); 93 } 94 private: 95 std::map<uint16_t, size_t> map_; 96 DISALLOW_COPY_AND_ASSIGN(PageCount); 97 }; 98 99 class Printer { 100 public: 101 Printer() : section_header_width_(ComputeHeaderWidth()) { 102 } 103 104 void PrintHeader() const { 105 std::cout << StringPrintf("%-*s %*s %*s %% of %% of", 106 section_header_width_, 107 kSectionHeader, 108 kPageCountWidth, 109 "resident", 110 kPageCountWidth, 111 "total" 112 ) 113 << std::endl; 114 std::cout << StringPrintf("%-*s %*s %*s sect. total", 115 section_header_width_, 116 "", 117 kPageCountWidth, 118 "pages", 119 kPageCountWidth, 120 "pages") 121 << std::endl; 122 } 123 124 void PrintOne(const char* name, 125 size_t resident, 126 size_t mapped, 127 double percent_of_section, 128 double percent_of_total) const { 129 // 6.2 is sufficient to print 0-100% with two decimal places of accuracy. 130 std::cout << StringPrintf("%-*s %*zd %*zd %6.2f %6.2f", 131 section_header_width_, 132 name, 133 kPageCountWidth, 134 resident, 135 kPageCountWidth, 136 mapped, 137 percent_of_section, 138 percent_of_total) 139 << std::endl; 140 } 141 142 void PrintSkipLine() const { std::cout << std::endl; } 143 144 // Computes the width of the section header column in the table (for fixed formatting). 145 static int ComputeHeaderWidth() { 146 int header_width = 0; 147 for (const auto& pair : kDexSectionInfoMap) { 148 const DexSectionInfo& section_info = pair.second; 149 header_width = std::max(header_width, static_cast<int>(section_info.name.length())); 150 } 151 return header_width; 152 } 153 154 private: 155 const int section_header_width_; 156 }; 157 158 static void PrintLetterKey() { 159 std::cout << "L pagetype" << std::endl; 160 for (const auto& pair : kDexSectionInfoMap) { 161 const DexSectionInfo& section_info = pair.second; 162 std::cout << section_info.letter << " " << section_info.name.c_str() << std::endl; 163 } 164 std::cout << "* (Executable page resident)" << std::endl; 165 std::cout << ". (Mapped page not resident)" << std::endl; 166 } 167 168 #ifdef ART_TARGET_ANDROID 169 static char PageTypeChar(uint16_t type) { 170 if (kDexSectionInfoMap.find(type) == kDexSectionInfoMap.end()) { 171 return '-'; 172 } 173 return kDexSectionInfoMap.find(type)->second.letter; 174 } 175 176 static uint16_t FindSectionTypeForPage(size_t page, 177 const std::vector<dex_ir::DexFileSection>& sections) { 178 for (const auto& section : sections) { 179 size_t first_page_of_section = section.offset / kPageSize; 180 // Only consider non-empty sections. 181 if (section.size == 0) { 182 continue; 183 } 184 // Attribute the page to the highest-offset section that starts before the page. 185 if (first_page_of_section <= page) { 186 return section.type; 187 } 188 } 189 // If there's no non-zero sized section with an offset below offset we're looking for, it 190 // must be the header. 191 return DexFile::kDexTypeHeaderItem; 192 } 193 194 static void ProcessPageMap(uint64_t* pagemap, 195 size_t start, 196 size_t end, 197 const std::vector<dex_ir::DexFileSection>& sections, 198 PageCount* page_counts) { 199 static constexpr size_t kLineLength = 32; 200 for (size_t page = start; page < end; ++page) { 201 char type_char = '.'; 202 if (PM_PAGEMAP_PRESENT(pagemap[page])) { 203 const size_t dex_page_offset = page - start; 204 uint16_t type = FindSectionTypeForPage(dex_page_offset, sections); 205 page_counts->Increment(type); 206 type_char = PageTypeChar(type); 207 } 208 if (g_verbose) { 209 std::cout << type_char; 210 if ((page - start) % kLineLength == kLineLength - 1) { 211 std::cout << std::endl; 212 } 213 } 214 } 215 if (g_verbose) { 216 if ((end - start) % kLineLength != 0) { 217 std::cout << std::endl; 218 } 219 } 220 } 221 222 static void DisplayDexStatistics(size_t start, 223 size_t end, 224 const PageCount& resident_pages, 225 const std::vector<dex_ir::DexFileSection>& sections, 226 Printer* printer) { 227 // Compute the total possible sizes for sections. 228 PageCount mapped_pages; 229 DCHECK_GE(end, start); 230 size_t total_mapped_pages = end - start; 231 if (total_mapped_pages == 0) { 232 return; 233 } 234 for (size_t page = start; page < end; ++page) { 235 const size_t dex_page_offset = page - start; 236 mapped_pages.Increment(FindSectionTypeForPage(dex_page_offset, sections)); 237 } 238 size_t total_resident_pages = 0; 239 printer->PrintHeader(); 240 for (size_t i = sections.size(); i > 0; --i) { 241 const dex_ir::DexFileSection& section = sections[i - 1]; 242 const uint16_t type = section.type; 243 const DexSectionInfo& section_info = kDexSectionInfoMap.find(type)->second; 244 size_t pages_resident = resident_pages.Get(type); 245 double percent_resident = 0; 246 if (mapped_pages.Get(type) > 0) { 247 percent_resident = 100.0 * pages_resident / mapped_pages.Get(type); 248 } 249 printer->PrintOne(section_info.name.c_str(), 250 pages_resident, 251 mapped_pages.Get(type), 252 percent_resident, 253 100.0 * pages_resident / total_mapped_pages); 254 total_resident_pages += pages_resident; 255 } 256 double percent_of_total = 100.0 * total_resident_pages / total_mapped_pages; 257 printer->PrintOne("GRAND TOTAL", 258 total_resident_pages, 259 total_mapped_pages, 260 percent_of_total, 261 percent_of_total); 262 printer->PrintSkipLine(); 263 } 264 265 static void ProcessOneDexMapping(uint64_t* pagemap, 266 uint64_t map_start, 267 const DexFile* dex_file, 268 uint64_t vdex_start, 269 Printer* printer) { 270 uint64_t dex_file_start = reinterpret_cast<uint64_t>(dex_file->Begin()); 271 size_t dex_file_size = dex_file->Size(); 272 if (dex_file_start < vdex_start) { 273 std::cerr << "Dex file start offset for " 274 << dex_file->GetLocation().c_str() 275 << " is incorrect: map start " 276 << StringPrintf("%" PRIx64 " > dex start %" PRIx64 "\n", map_start, dex_file_start) 277 << std::endl; 278 return; 279 } 280 uint64_t start_page = (dex_file_start - vdex_start) / kPageSize; 281 uint64_t start_address = start_page * kPageSize; 282 uint64_t end_page = RoundUp(start_address + dex_file_size, kPageSize) / kPageSize; 283 std::cout << "DEX " 284 << dex_file->GetLocation().c_str() 285 << StringPrintf(": %" PRIx64 "-%" PRIx64, 286 map_start + start_page * kPageSize, 287 map_start + end_page * kPageSize) 288 << std::endl; 289 // Build a list of the dex file section types, sorted from highest offset to lowest. 290 std::vector<dex_ir::DexFileSection> sections; 291 { 292 std::unique_ptr<dex_ir::Header> header(dex_ir::DexIrBuilder(*dex_file)); 293 sections = dex_ir::GetSortedDexFileSections(header.get(), 294 dex_ir::SortDirection::kSortDescending); 295 } 296 PageCount section_resident_pages; 297 ProcessPageMap(pagemap, start_page, end_page, sections, §ion_resident_pages); 298 DisplayDexStatistics(start_page, end_page, section_resident_pages, sections, printer); 299 } 300 301 static bool IsVdexFileMapping(const std::string& mapped_name) { 302 // Confirm that the map is from a vdex file. 303 static const char* suffixes[] = { ".vdex" }; 304 for (const char* suffix : suffixes) { 305 size_t match_loc = mapped_name.find(suffix); 306 if (match_loc != std::string::npos && mapped_name.length() == match_loc + strlen(suffix)) { 307 return true; 308 } 309 } 310 return false; 311 } 312 313 static bool DisplayMappingIfFromVdexFile(pm_map_t* map, Printer* printer) { 314 std::string vdex_name = pm_map_name(map); 315 // Extract all the dex files from the vdex file. 316 std::string error_msg; 317 std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_name, 318 false /*writeable*/, 319 false /*low_4gb*/, 320 false /*unquicken */, 321 &error_msg /*out*/)); 322 if (vdex == nullptr) { 323 std::cerr << "Could not open vdex file " 324 << vdex_name 325 << ": error " 326 << error_msg 327 << std::endl; 328 return false; 329 } 330 331 std::vector<std::unique_ptr<const DexFile>> dex_files; 332 if (!vdex->OpenAllDexFiles(&dex_files, &error_msg)) { 333 std::cerr << "Dex files could not be opened for " 334 << vdex_name 335 << ": error " 336 << error_msg 337 << std::endl; 338 return false; 339 } 340 // Open the page mapping (one uint64_t per page) for the entire vdex mapping. 341 uint64_t* pagemap; 342 size_t len; 343 if (pm_map_pagemap(map, &pagemap, &len) != 0) { 344 std::cerr << "Error creating pagemap." << std::endl; 345 return false; 346 } 347 // Process the dex files. 348 std::cout << "MAPPING " 349 << pm_map_name(map) 350 << StringPrintf(": %" PRIx64 "-%" PRIx64, pm_map_start(map), pm_map_end(map)) 351 << std::endl; 352 for (const auto& dex_file : dex_files) { 353 ProcessOneDexMapping(pagemap, 354 pm_map_start(map), 355 dex_file.get(), 356 reinterpret_cast<uint64_t>(vdex->Begin()), 357 printer); 358 } 359 free(pagemap); 360 return true; 361 } 362 363 static void ProcessOneOatMapping(uint64_t* pagemap, size_t size, Printer* printer) { 364 static constexpr size_t kLineLength = 32; 365 size_t resident_page_count = 0; 366 for (size_t page = 0; page < size; ++page) { 367 char type_char = '.'; 368 if (PM_PAGEMAP_PRESENT(pagemap[page])) { 369 ++resident_page_count; 370 type_char = '*'; 371 } 372 if (g_verbose) { 373 std::cout << type_char; 374 if (page % kLineLength == kLineLength - 1) { 375 std::cout << std::endl; 376 } 377 } 378 } 379 if (g_verbose) { 380 if (size % kLineLength != 0) { 381 std::cout << std::endl; 382 } 383 } 384 double percent_of_total = 100.0 * resident_page_count / size; 385 printer->PrintHeader(); 386 printer->PrintOne("EXECUTABLE", resident_page_count, size, percent_of_total, percent_of_total); 387 printer->PrintSkipLine(); 388 } 389 390 static bool IsOatFileMapping(const std::string& mapped_name) { 391 // Confirm that the map is from an oat file. 392 static const char* suffixes[] = { ".odex", ".oat" }; 393 for (const char* suffix : suffixes) { 394 size_t match_loc = mapped_name.find(suffix); 395 if (match_loc != std::string::npos && mapped_name.length() == match_loc + strlen(suffix)) { 396 return true; 397 } 398 } 399 return false; 400 } 401 402 static bool DisplayMappingIfFromOatFile(pm_map_t* map, Printer* printer) { 403 // Open the page mapping (one uint64_t per page) for the entire vdex mapping. 404 uint64_t* pagemap; 405 size_t len; 406 if (pm_map_pagemap(map, &pagemap, &len) != 0) { 407 std::cerr << "Error creating pagemap." << std::endl; 408 return false; 409 } 410 // Process the dex files. 411 std::cout << "MAPPING " 412 << pm_map_name(map) 413 << StringPrintf(": %" PRIx64 "-%" PRIx64, pm_map_start(map), pm_map_end(map)) 414 << std::endl; 415 ProcessOneOatMapping(pagemap, len, printer); 416 free(pagemap); 417 return true; 418 } 419 420 static bool FilterByNameContains(const std::string& mapped_file_name, 421 const std::vector<std::string>& name_filters) { 422 // If no filters were set, everything matches. 423 if (name_filters.empty()) { 424 return true; 425 } 426 for (const auto& name_contains : name_filters) { 427 if (mapped_file_name.find(name_contains) != std::string::npos) { 428 return true; 429 } 430 } 431 return false; 432 } 433 #endif 434 435 static void Usage(const char* cmd) { 436 std::cout << "Usage: " << cmd << " [options] pid" << std::endl 437 << " --contains=<string>: Display sections containing string." << std::endl 438 << " --help: Shows this message." << std::endl 439 << " --verbose: Makes displays verbose." << std::endl; 440 PrintLetterKey(); 441 } 442 443 static int DexDiagMain(int argc, char* argv[]) { 444 if (argc < 2) { 445 Usage(argv[0]); 446 return EXIT_FAILURE; 447 } 448 449 std::vector<std::string> name_filters; 450 // TODO: add option to track usage by class name, etc. 451 for (int i = 1; i < argc - 1; ++i) { 452 const StringPiece option(argv[i]); 453 if (option == "--help") { 454 Usage(argv[0]); 455 return EXIT_SUCCESS; 456 } else if (option == "--verbose") { 457 g_verbose = true; 458 } else if (option.starts_with("--contains=")) { 459 std::string contains(option.substr(strlen("--contains=")).data()); 460 name_filters.push_back(contains); 461 } else { 462 Usage(argv[0]); 463 return EXIT_FAILURE; 464 } 465 } 466 467 // Art specific set up. 468 InitLogging(argv, Runtime::Abort); 469 MemMap::Init(); 470 471 #ifdef ART_TARGET_ANDROID 472 pid_t pid; 473 char* endptr; 474 pid = (pid_t)strtol(argv[argc - 1], &endptr, 10); 475 if (*endptr != '\0' || kill(pid, 0) != 0) { 476 std::cerr << StringPrintf("Invalid PID \"%s\".\n", argv[argc - 1]) << std::endl; 477 return EXIT_FAILURE; 478 } 479 480 // get libpagemap kernel information. 481 pm_kernel_t* ker; 482 if (pm_kernel_create(&ker) != 0) { 483 std::cerr << "Error creating kernel interface -- does this kernel have pagemap?" << std::endl; 484 return EXIT_FAILURE; 485 } 486 487 // get libpagemap process information. 488 pm_process_t* proc; 489 if (pm_process_create(ker, pid, &proc) != 0) { 490 std::cerr << "Error creating process interface -- does process " 491 << pid 492 << " really exist?" 493 << std::endl; 494 return EXIT_FAILURE; 495 } 496 497 // Get the set of mappings by the specified process. 498 pm_map_t** maps; 499 size_t num_maps; 500 if (pm_process_maps(proc, &maps, &num_maps) != 0) { 501 std::cerr << "Error listing maps." << std::endl; 502 return EXIT_FAILURE; 503 } 504 505 bool match_found = false; 506 // Process the mappings that are due to vdex or oat files. 507 Printer printer; 508 for (size_t i = 0; i < num_maps; ++i) { 509 std::string mapped_file_name = pm_map_name(maps[i]); 510 // Filter by name contains options (if any). 511 if (!FilterByNameContains(mapped_file_name, name_filters)) { 512 continue; 513 } 514 if (IsVdexFileMapping(mapped_file_name)) { 515 if (!DisplayMappingIfFromVdexFile(maps[i], &printer)) { 516 return EXIT_FAILURE; 517 } 518 match_found = true; 519 } else if (IsOatFileMapping(mapped_file_name)) { 520 if (!DisplayMappingIfFromOatFile(maps[i], &printer)) { 521 return EXIT_FAILURE; 522 } 523 match_found = true; 524 } 525 } 526 if (!match_found) { 527 std::cerr << "No relevant memory maps were found." << std::endl; 528 return EXIT_FAILURE; 529 } 530 #endif 531 532 return EXIT_SUCCESS; 533 } 534 535 } // namespace art 536 537 int main(int argc, char* argv[]) { 538 return art::DexDiagMain(argc, argv); 539 } 540