diff options
-rw-r--r-- | dexlayout/Android.bp | 13 | ||||
-rw-r--r-- | dexlayout/dexdiag.cc | 405 | ||||
-rw-r--r-- | runtime/vdex_file.cc | 26 | ||||
-rw-r--r-- | runtime/vdex_file.h | 8 |
4 files changed, 452 insertions, 0 deletions
diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp index cf523ece7a..e26d051d9d 100644 --- a/dexlayout/Android.bp +++ b/dexlayout/Android.bp @@ -56,3 +56,16 @@ art_cc_test { defaults: ["art_gtest_defaults"], srcs: ["dexlayout_test.cc"], } + +art_cc_binary { + name: "dexdiag", + host_supported: false, + srcs: ["dexdiag.cc"], + cflags: ["-Wall"], + shared_libs: [ + "libart", + "libart-dexlayout", + "libpagemap", + ], +} + diff --git a/dexlayout/dexdiag.cc b/dexlayout/dexdiag.cc new file mode 100644 index 0000000000..211bfdf9ec --- /dev/null +++ b/dexlayout/dexdiag.cc @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include <iostream> +#include <memory> + +#include "android-base/stringprintf.h" + +#include "dex_file.h" +#include "dex_ir.h" +#include "dex_ir_builder.h" +#include "pagemap/pagemap.h" +#include "runtime.h" +#include "vdex_file.h" + +namespace art { + +using android::base::StringPrintf; + +static constexpr size_t kLineLength = 32; + +static bool g_show_key = false; +static bool g_verbose = false; +static bool g_show_statistics = false; + +struct DexSectionInfo { + public: + std::string name; + char letter; +}; + +static const std::map<uint16_t, DexSectionInfo> kDexSectionInfoMap = { + { DexFile::kDexTypeHeaderItem, { "Header", 'H' } }, + { DexFile::kDexTypeStringIdItem, { "StringId", 'S' } }, + { DexFile::kDexTypeTypeIdItem, { "TypeId", 'T' } }, + { DexFile::kDexTypeProtoIdItem, { "ProtoId", 'P' } }, + { DexFile::kDexTypeFieldIdItem, { "FieldId", 'F' } }, + { DexFile::kDexTypeMethodIdItem, { "MethodId", 'M' } }, + { DexFile::kDexTypeClassDefItem, { "ClassDef", 'C' } }, + { DexFile::kDexTypeCallSiteIdItem, { "CallSiteId", 'z' } }, + { DexFile::kDexTypeMethodHandleItem, { "MethodHandle", 'Z' } }, + { DexFile::kDexTypeMapList, { "TypeMap", 'L' } }, + { DexFile::kDexTypeTypeList, { "TypeList", 't' } }, + { DexFile::kDexTypeAnnotationSetRefList, { "AnnotationSetReferenceItem", '1' } }, + { DexFile::kDexTypeAnnotationSetItem, { "AnnotationSetItem", '2' } }, + { DexFile::kDexTypeClassDataItem, { "ClassData", 'c' } }, + { DexFile::kDexTypeCodeItem, { "CodeItem", 'X' } }, + { DexFile::kDexTypeStringDataItem, { "StringData", 's' } }, + { DexFile::kDexTypeDebugInfoItem, { "DebugInfo", 'D' } }, + { DexFile::kDexTypeAnnotationItem, { "AnnotationItem", '3' } }, + { DexFile::kDexTypeEncodedArrayItem, { "EncodedArrayItem", 'E' } }, + { DexFile::kDexTypeAnnotationsDirectoryItem, { "AnnotationsDirectoryItem", '4' } } +}; + +class PageCount { + public: + PageCount() { + for (auto it = kDexSectionInfoMap.begin(); it != kDexSectionInfoMap.end(); ++it) { + map_[it->first] = 0; + } + } + void Increment(uint16_t type) { + map_[type]++; + } + size_t Get(uint16_t type) const { + return map_.at(type); + } + private: + std::map<uint16_t, size_t> map_; + DISALLOW_COPY_AND_ASSIGN(PageCount); +}; + +static void PrintLetterKey() { + std::cout << "letter section_type" << std::endl; + for (const auto& p : kDexSectionInfoMap) { + const DexSectionInfo& section_info = p.second; + std::cout << section_info.letter << " " << section_info.name.c_str() << std::endl; + } +} + +static char PageTypeChar(uint16_t type) { + if (kDexSectionInfoMap.find(type) == kDexSectionInfoMap.end()) { + return '-'; + } + return kDexSectionInfoMap.find(type)->second.letter; +} + +static uint16_t FindSectionTypeForPage(size_t page, + const std::vector<dex_ir::DexFileSection>& sections) { + for (const auto& section : sections) { + size_t first_page_of_section = section.offset / kPageSize; + // Only consider non-empty sections. + if (section.size == 0) { + continue; + } + // Attribute the page to the highest-offset section that starts before the page. + if (first_page_of_section <= page) { + return section.type; + } + } + // If there's no non-zero sized section with an offset below offset we're looking for, it + // must be the header. + return DexFile::kDexTypeHeaderItem; +} + +static void ProcessPageMap(uint64_t* pagemap, + size_t start, + size_t end, + const std::vector<dex_ir::DexFileSection>& sections, + PageCount* page_counts) { + for (size_t page = start; page < end; ++page) { + char type_char = '.'; + if (PM_PAGEMAP_PRESENT(pagemap[page])) { + uint16_t type = FindSectionTypeForPage(page, sections); + page_counts->Increment(type); + type_char = PageTypeChar(type); + } + if (g_verbose) { + std::cout << type_char; + if ((page - start) % kLineLength == kLineLength - 1) { + std::cout << std::endl; + } + } + } + if (g_verbose) { + if ((end - start) % kLineLength != 0) { + std::cout << std::endl; + } + } +} + +static void DisplayDexStatistics(size_t start, + size_t end, + const PageCount& resident_pages, + const std::vector<dex_ir::DexFileSection>& sections) { + // Compute the total possible sizes for sections. + PageCount mapped_pages; + DCHECK_GE(end, start); + size_t total_mapped_pages = end - start; + if (total_mapped_pages == 0) { + return; + } + for (size_t page = start; page < end; ++page) { + mapped_pages.Increment(FindSectionTypeForPage(page, sections)); + } + size_t total_resident_pages = 0; + // Compute the width of the section header column in the table (for fixed formatting). + int section_header_width = 0; + for (const auto& section_info : kDexSectionInfoMap) { + section_header_width = std::max(section_header_width, + static_cast<int>(section_info.second.name.length())); + } + // The width needed to print a file page offset (32-bit). + static constexpr int kPageCountWidth = + static_cast<int>(std::numeric_limits<uint32_t>::digits10); + // Display the sections. + static constexpr char kSectionHeader[] = "Section name"; + std::cout << StringPrintf("%-*s %*s %*s %% of %% of", + section_header_width, + kSectionHeader, + kPageCountWidth, + "resident", + kPageCountWidth, + "total" + ) + << std::endl; + std::cout << StringPrintf("%-*s %*s %*s sect. total", + section_header_width, + "", + kPageCountWidth, + "pages", + kPageCountWidth, + "pages") + << std::endl; + for (size_t i = sections.size(); i > 0; --i) { + const dex_ir::DexFileSection& section = sections[i - 1]; + const uint16_t type = section.type; + const DexSectionInfo& section_info = kDexSectionInfoMap.find(type)->second; + size_t pages_resident = resident_pages.Get(type); + double percent_resident = 0; + if (mapped_pages.Get(type) > 0) { + percent_resident = 100.0 * pages_resident / mapped_pages.Get(type); + } + // 6.2 is sufficient to print 0-100% with two decimal places of accuracy. + std::cout << StringPrintf("%-*s %*zd %*zd %6.2f %6.2f", + section_header_width, + section_info.name.c_str(), + kPageCountWidth, + pages_resident, + kPageCountWidth, + mapped_pages.Get(type), + percent_resident, + 100.0 * pages_resident / total_mapped_pages) + << std::endl; + total_resident_pages += pages_resident; + } + std::cout << StringPrintf("%-*s %*zd %*zd %6.2f", + section_header_width, + "GRAND TOTAL", + kPageCountWidth, + total_resident_pages, + kPageCountWidth, + total_mapped_pages, + 100.0 * total_resident_pages / total_mapped_pages) + << std::endl + << std::endl; +} + +static void ProcessOneDexMapping(uint64_t* pagemap, + uint64_t map_start, + const DexFile* dex_file, + uint64_t vdex_start) { + uint64_t dex_file_start = reinterpret_cast<uint64_t>(dex_file->Begin()); + size_t dex_file_size = dex_file->Size(); + if (dex_file_start < vdex_start) { + std::cerr << "Dex file start offset for " + << dex_file->GetLocation().c_str() + << " is incorrect: map start " + << StringPrintf("%zx > dex start %zx\n", map_start, dex_file_start) + << std::endl; + return; + } + uint64_t start = (dex_file_start - vdex_start) / kPageSize; + uint64_t end = RoundUp(start + dex_file_size, kPageSize) / kPageSize; + std::cout << "DEX " + << dex_file->GetLocation().c_str() + << StringPrintf(": %zx-%zx", + map_start + start * kPageSize, + map_start + end * kPageSize) + << std::endl; + // Build a list of the dex file section types, sorted from highest offset to lowest. + std::vector<dex_ir::DexFileSection> sections; + { + std::unique_ptr<dex_ir::Header> header(dex_ir::DexIrBuilder(*dex_file)); + sections = dex_ir::GetSortedDexFileSections(header.get(), + dex_ir::SortDirection::kSortDescending); + } + PageCount section_resident_pages; + ProcessPageMap(pagemap, start, end, sections, §ion_resident_pages); + if (g_show_statistics) { + DisplayDexStatistics(start, end, section_resident_pages, sections); + } +} + +static bool DisplayMappingIfFromVdexFile(pm_map_t* map) { + // Confirm that the map is from a vdex file. + static const char* suffixes[] = { ".vdex" }; + std::string vdex_name; + bool found = false; + for (size_t j = 0; j < sizeof(suffixes) / sizeof(suffixes[0]); ++j) { + if (strstr(pm_map_name(map), suffixes[j]) != nullptr) { + vdex_name = pm_map_name(map); + found = true; + break; + } + } + if (!found) { + return true; + } + // Extract all the dex files from the vdex file. + std::string error_msg; + std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_name, + false /*writeable*/, + false /*low_4gb*/, + &error_msg /*out*/)); + if (vdex == nullptr) { + std::cerr << "Could not open vdex file " + << vdex_name.c_str() + << ": error " + << error_msg.c_str() + << std::endl; + return false; + } + + std::vector<std::unique_ptr<const DexFile>> dex_files; + if (!vdex->OpenAllDexFiles(&dex_files, &error_msg)) { + std::cerr << "Dex files could not be opened for " + << vdex_name.c_str() + << ": error " + << error_msg.c_str() + << std::endl; + } + // Open the page mapping (one uint64_t per page) for the entire vdex mapping. + uint64_t* pagemap; + size_t len; + if (pm_map_pagemap(map, &pagemap, &len) != 0) { + std::cerr << "Error creating pagemap." << std::endl; + return false; + } + // Process the dex files. + std::cout << "MAPPING " + << pm_map_name(map) + << StringPrintf(": %zx-%zx", pm_map_start(map), pm_map_end(map)) + << std::endl; + for (const auto& dex_file : dex_files) { + ProcessOneDexMapping(pagemap, + pm_map_start(map), + dex_file.get(), + reinterpret_cast<uint64_t>(vdex->Begin())); + } + free(pagemap); + return true; +} + + +static void Usage(const char* cmd) { + std::cerr << "Usage: " << cmd << " [-k] [-s] [-v] pid" << std::endl + << " -k Shows a key to verbose display characters." << std::endl + << " -s Shows section statistics for individual dex files." << std::endl + << " -v Verbosely displays resident pages for dex files." << std::endl; +} + +static int DexDiagMain(int argc, char* argv[]) { + if (argc < 2) { + Usage(argv[0]); + return EXIT_FAILURE; + } + + // TODO: add option to track usage by class name, etc. + for (int i = 1; i < argc - 1; ++i) { + if (strcmp(argv[i], "-k") == 0) { + g_show_key = true; + } else if (strcmp(argv[i], "-s") == 0) { + g_show_statistics = true; + } else if (strcmp(argv[i], "-v") == 0) { + g_verbose = true; + } else { + Usage(argv[0]); + return EXIT_FAILURE; + } + } + + // Art specific set up. + InitLogging(argv, Runtime::Aborter); + MemMap::Init(); + + pid_t pid; + char* endptr; + pid = (pid_t)strtol(argv[argc - 1], &endptr, 10); + if (*endptr != '\0' || kill(pid, 0) != 0) { + std::cerr << StringPrintf("Invalid PID \"%s\".\n", argv[argc - 1]) << std::endl; + return EXIT_FAILURE; + } + + // get libpagemap kernel information. + pm_kernel_t* ker; + if (pm_kernel_create(&ker) != 0) { + std::cerr << "Error creating kernel interface -- does this kernel have pagemap?" << std::endl; + return EXIT_FAILURE; + } + + // get libpagemap process information. + pm_process_t* proc; + if (pm_process_create(ker, pid, &proc) != 0) { + std::cerr << "Error creating process interface -- does process " + << pid + << " really exist?" + << std::endl; + return EXIT_FAILURE; + } + + // Get the set of mappings by the specified process. + pm_map_t** maps; + size_t num_maps; + if (pm_process_maps(proc, &maps, &num_maps) != 0) { + std::cerr << "Error listing maps." << std::endl; + return EXIT_FAILURE; + } + + // Process the mappings that are due to DEX files. + for (size_t i = 0; i < num_maps; ++i) { + if (!DisplayMappingIfFromVdexFile(maps[i])) { + return EXIT_FAILURE; + } + } + + if (g_show_key) { + PrintLetterKey(); + } + return 0; +} + +} // namespace art + +int main(int argc, char* argv[]) { + return art::DexDiagMain(argc, argv); +} diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index 2481c8ba46..9ff104bbe1 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -120,4 +120,30 @@ const uint8_t* VdexFile::GetNextDexFileData(const uint8_t* cursor) const { } } +bool VdexFile::OpenAllDexFiles(std::vector<std::unique_ptr<const DexFile>>* dex_files, + std::string* error_msg) { + size_t i = 0; + for (const uint8_t* dex_file_start = GetNextDexFileData(nullptr); + dex_file_start != nullptr; + dex_file_start = GetNextDexFileData(dex_file_start), ++i) { + size_t size = reinterpret_cast<const DexFile::Header*>(dex_file_start)->file_size_; + // TODO: Supply the location information for a vdex file. + static constexpr char kVdexLocation[] = ""; + std::string location = DexFile::GetMultiDexLocation(i, kVdexLocation); + std::unique_ptr<const DexFile> dex(DexFile::Open(dex_file_start, + size, + location, + GetLocationChecksum(i), + nullptr /*oat_dex_file*/, + false /*verify*/, + false /*verify_checksum*/, + error_msg)); + if (dex == nullptr) { + return false; + } + dex_files->push_back(std::move(dex)); + } + return true; +} + } // namespace art diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index 898d07d22c..9840555bbd 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -27,6 +27,8 @@ namespace art { +class DexFile; + // VDEX files contain extracted DEX files. The VdexFile class maps the file to // memory and provides tools for accessing its individual sections. // @@ -122,6 +124,12 @@ class VdexFile { return reinterpret_cast<const uint32_t*>(Begin() + sizeof(Header))[dex_file_index]; } + // Opens all the dex files contained in this vdex file. This is currently + // used for dumping tools only, and has not been tested for use by the + // remainder of the runtime. + bool OpenAllDexFiles(std::vector<std::unique_ptr<const DexFile>>* dex_files, + std::string* error_msg); + private: explicit VdexFile(MemMap* mmap) : mmap_(mmap) {} |