summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dexlayout/Android.bp13
-rw-r--r--dexlayout/dexdiag.cc405
-rw-r--r--runtime/vdex_file.cc26
-rw-r--r--runtime/vdex_file.h8
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, &section_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) {}