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