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