From 9335749d25f8826c72ea7ea4d470ee8bc171fa87 Mon Sep 17 00:00:00 2001 From: David Sehr Date: Thu, 9 Mar 2017 08:02:44 -0800 Subject: Add section size statistics Add an option to display the size of dex file sections for easier analysis. Bug: none Test: test-art-host Change-Id: I779f776657ad79e7a50c18c2b9486e6778c70f13 --- dexlayout/dex_visualize.cc | 121 +++++++++++++++++++++++++++++++------------- dexlayout/dex_visualize.h | 2 + dexlayout/dexlayout.cc | 5 ++ dexlayout/dexlayout.h | 1 + dexlayout/dexlayout_main.cc | 7 ++- 5 files changed, 99 insertions(+), 37 deletions(-) diff --git a/dexlayout/dex_visualize.cc b/dexlayout/dex_visualize.cc index 75d47e4013..8997146c74 100644 --- a/dexlayout/dex_visualize.cc +++ b/dexlayout/dex_visualize.cc @@ -35,6 +35,12 @@ namespace art { +std::string MultidexName(const std::string& prefix, + size_t dex_file_index, + const std::string& suffix) { + return prefix + ((dex_file_index > 0) ? std::to_string(dex_file_index + 1) : "") + suffix; +} + struct FileSection { public: std::string name_; @@ -43,8 +49,22 @@ struct FileSection { std::function offset_fn_; }; +static uint32_t HeaderOffset(const dex_ir::Collections& collections ATTRIBUTE_UNUSED) { + return 0; +} + +static uint32_t HeaderSize(const dex_ir::Collections& collections ATTRIBUTE_UNUSED) { + // Size is in elements, so there is only one header. + return 1; +} + static const std::vector kFileSections = { { + "Header", + DexFile::kDexTypeHeaderItem, + &HeaderSize, + &HeaderOffset, + }, { "StringId", DexFile::kDexTypeStringIdItem, &dex_ir::Collections::StringIdsSize, @@ -127,58 +147,71 @@ static const std::vector kFileSections = { } }; +static constexpr bool kSortAscending = false; +static constexpr bool kSortDescending = true; + +static std::vector GetSortedSections( + const dex_ir::Collections& collections, + bool sort_descending) { + std::vector sorted_sections; + // Build the table that will map from offset to color + for (const FileSection& s : kFileSections) { + sorted_sections.push_back(&s); + } + // Sort by offset. + std::sort(sorted_sections.begin(), + sorted_sections.end(), + [&](const FileSection* a, const FileSection* b) { + if (sort_descending) { + return a->offset_fn_(collections) > b->offset_fn_(collections); + } else { + return a->offset_fn_(collections) < b->offset_fn_(collections); + } + }); + return sorted_sections; +} + class Dumper { public: // Colors are based on the type of the section in MapList. - Dumper(const dex_ir::Collections& collections, size_t dex_file_index) { - // Build the table that will map from offset to color - table_.emplace_back(DexFile::kDexTypeHeaderItem, 0u); - for (const FileSection& s : kFileSections) { - table_.emplace_back(s.type_, s.offset_fn_(collections)); - } - // Sort into descending order by offset. - std::sort(table_.begin(), - table_.end(), - [](const SectionColor& a, const SectionColor& b) { return a.offset_ > b.offset_; }); + explicit Dumper(const dex_ir::Collections& collections) + : collections_(collections), out_file_(nullptr), + sorted_sections_(GetSortedSections(collections, kSortDescending)) { } + + bool OpenAndPrintHeader(size_t dex_index) { // Open the file and emit the gnuplot prologue. - std::string dex_file_name("classes"); - std::string out_file_base_name("layout"); - if (dex_file_index > 0) { - out_file_base_name += std::to_string(dex_file_index + 1); - dex_file_name += std::to_string(dex_file_index + 1); + out_file_ = fopen(MultidexName("layout", dex_index, ".gnuplot").c_str(), "w"); + if (out_file_ == nullptr) { + return false; } - dex_file_name += ".dex"; - std::string out_file_name(out_file_base_name + ".gnuplot"); - std::string png_file_name(out_file_base_name + ".png"); - out_file_ = fopen(out_file_name.c_str(), "w"); fprintf(out_file_, "set terminal png size 1920,1080\n"); - fprintf(out_file_, "set output \"%s\"\n", png_file_name.c_str()); - fprintf(out_file_, "set title \"%s\"\n", dex_file_name.c_str()); + fprintf(out_file_, "set output \"%s\"\n", MultidexName("layout", dex_index, ".png").c_str()); + fprintf(out_file_, "set title \"%s\"\n", MultidexName("classes", dex_index, ".dex").c_str()); fprintf(out_file_, "set xlabel \"Page offset into dex\"\n"); fprintf(out_file_, "set ylabel \"ClassDef index\"\n"); fprintf(out_file_, "set xtics rotate out ("); - fprintf(out_file_, "\"Header\" %d, ", 0); bool printed_one = false; for (const FileSection& s : kFileSections) { - if (s.size_fn_(collections) > 0) { + if (s.size_fn_(collections_) > 0) { if (printed_one) { fprintf(out_file_, ", "); } - fprintf(out_file_, "\"%s\" %d", s.name_.c_str(), s.offset_fn_(collections) / kPageSize); + fprintf(out_file_, "\"%s\" %d", s.name_.c_str(), s.offset_fn_(collections_) / kPageSize); printed_one = true; } } fprintf(out_file_, ")\n"); fprintf(out_file_, "plot \"-\" using 1:2:3:4:5 with vector nohead linewidth 1 lc variable notitle\n"); + return true; } int GetColor(uint32_t offset) const { // The dread linear search to find the right section for the reference. uint16_t section = 0; - for (uint16_t i = 0; i < table_.size(); ++i) { - if (table_[i].offset_ < offset) { - section = table_[i].type_; + for (const FileSection* file_section : sorted_sections_) { + if (file_section->offset_fn_(collections_) < offset) { + section = file_section->type_; break; } } @@ -308,13 +341,6 @@ class Dumper { } private: - struct SectionColor { - public: - SectionColor(uint16_t type, uint32_t offset) : type_(type), offset_(offset) { } - uint16_t type_; - uint32_t offset_; - }; - using ColorMapType = std::map; const ColorMapType kColorMap = { { DexFile::kDexTypeHeaderItem, 1 }, @@ -336,8 +362,9 @@ class Dumper { { DexFile::kDexTypeAnnotationsDirectoryItem, 16 } }; - std::vector table_; + const dex_ir::Collections& collections_; FILE* out_file_; + std::vector sorted_sections_; DISALLOW_COPY_AND_ASSIGN(Dumper); }; @@ -350,7 +377,11 @@ void VisualizeDexLayout(dex_ir::Header* header, const DexFile* dex_file, size_t dex_file_index, ProfileCompilationInfo* profile_info) { - std::unique_ptr dumper(new Dumper(header->GetCollections(), dex_file_index)); + std::unique_ptr dumper(new Dumper(header->GetCollections())); + if (!dumper->OpenAndPrintHeader(dex_file_index)) { + fprintf(stderr, "Could not open output file.\n"); + return; + } const uint32_t class_defs_size = header->GetCollections().ClassDefsSize(); for (uint32_t class_index = 0; class_index < class_defs_size; class_index++) { @@ -401,4 +432,22 @@ void VisualizeDexLayout(dex_ir::Header* header, } // for } +/* + * Dumps the offset and size of sections within the file. + */ +void ShowDexSectionStatistics(dex_ir::Header* header, size_t dex_file_index) { + // Compute the (multidex) class file name). + fprintf(stdout, "%s\n", MultidexName("classes", dex_file_index, ".dex").c_str()); + fprintf(stdout, "section offset items\n"); + const dex_ir::Collections& collections = header->GetCollections(); + std::vector sorted_sections(GetSortedSections(collections, kSortAscending)); + for (const FileSection* file_section : sorted_sections) { + fprintf(stdout, "%-10s 0x%08x 0x%08x\n", + file_section->name_.c_str(), + file_section->offset_fn_(collections), + file_section->size_fn_(collections)); + } + fprintf(stdout, "\n"); +} + } // namespace art diff --git a/dexlayout/dex_visualize.h b/dexlayout/dex_visualize.h index 09f830681b..a1aa2cd8bc 100644 --- a/dexlayout/dex_visualize.h +++ b/dexlayout/dex_visualize.h @@ -38,6 +38,8 @@ void VisualizeDexLayout(dex_ir::Header* header, size_t dex_file_index, ProfileCompilationInfo* profile_info); +void ShowDexSectionStatistics(dex_ir::Header* header, size_t dex_file_index); + } // namespace art #endif // ART_DEXLAYOUT_DEX_VISUALIZE_H_ diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index 4aa8b82ec7..005b7874d9 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -1745,6 +1745,11 @@ void DexLayout::ProcessDexFile(const char* file_name, return; } + if (options_.show_section_statistics_) { + ShowDexSectionStatistics(header_, dex_file_index); + return; + } + // Dump dex file. if (options_.dump_) { DumpDexFile(); diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h index 391870644a..74b525372e 100644 --- a/dexlayout/dexlayout.h +++ b/dexlayout/dexlayout.h @@ -56,6 +56,7 @@ class Options { bool show_annotations_ = false; bool show_file_headers_ = false; bool show_section_headers_ = false; + bool show_section_statistics_ = false; bool verbose_ = false; bool visualize_pattern_ = false; OutputFormat output_format_ = kOutputPlain; diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc index ad599aed93..3eac660eca 100644 --- a/dexlayout/dexlayout_main.cc +++ b/dexlayout/dexlayout_main.cc @@ -57,6 +57,7 @@ static void Usage(void) { fprintf(stderr, " -o : output file name (defaults to stdout)\n"); fprintf(stderr, " -p : profile file name (defaults to no profile)\n"); fprintf(stderr, " -s : visualize reference pattern\n"); + fprintf(stderr, " -t : display file section sizes\n"); fprintf(stderr, " -w : output dex directory \n"); } @@ -75,7 +76,7 @@ int DexlayoutDriver(int argc, char** argv) { // Parse all arguments. while (1) { - const int ic = getopt(argc, argv, "abcdefghil:mo:p:sw:"); + const int ic = getopt(argc, argv, "abcdefghil:mo:p:stw:"); if (ic < 0) { break; // done } @@ -127,6 +128,10 @@ int DexlayoutDriver(int argc, char** argv) { options.visualize_pattern_ = true; options.verbose_ = false; break; + case 't': // display section statistics + options.show_section_statistics_ = true; + options.verbose_ = false; + break; case 'w': // output dex files directory options.output_dex_directory_ = optarg; break; -- cgit v1.2.3-59-g8ed1b