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