blob: 211bfdf9ecab956c363a58087bc0be0c0044d53f [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>
24
25#include "android-base/stringprintf.h"
26
27#include "dex_file.h"
28#include "dex_ir.h"
29#include "dex_ir_builder.h"
30#include "pagemap/pagemap.h"
31#include "runtime.h"
32#include "vdex_file.h"
33
34namespace art {
35
36using android::base::StringPrintf;
37
38static constexpr size_t kLineLength = 32;
39
40static bool g_show_key = false;
41static bool g_verbose = false;
42static bool g_show_statistics = false;
43
44struct DexSectionInfo {
45 public:
46 std::string name;
47 char letter;
48};
49
50static const std::map<uint16_t, DexSectionInfo> kDexSectionInfoMap = {
51 { DexFile::kDexTypeHeaderItem, { "Header", 'H' } },
52 { DexFile::kDexTypeStringIdItem, { "StringId", 'S' } },
53 { DexFile::kDexTypeTypeIdItem, { "TypeId", 'T' } },
54 { DexFile::kDexTypeProtoIdItem, { "ProtoId", 'P' } },
55 { DexFile::kDexTypeFieldIdItem, { "FieldId", 'F' } },
56 { DexFile::kDexTypeMethodIdItem, { "MethodId", 'M' } },
57 { DexFile::kDexTypeClassDefItem, { "ClassDef", 'C' } },
58 { DexFile::kDexTypeCallSiteIdItem, { "CallSiteId", 'z' } },
59 { DexFile::kDexTypeMethodHandleItem, { "MethodHandle", 'Z' } },
60 { DexFile::kDexTypeMapList, { "TypeMap", 'L' } },
61 { DexFile::kDexTypeTypeList, { "TypeList", 't' } },
62 { DexFile::kDexTypeAnnotationSetRefList, { "AnnotationSetReferenceItem", '1' } },
63 { DexFile::kDexTypeAnnotationSetItem, { "AnnotationSetItem", '2' } },
64 { DexFile::kDexTypeClassDataItem, { "ClassData", 'c' } },
65 { DexFile::kDexTypeCodeItem, { "CodeItem", 'X' } },
66 { DexFile::kDexTypeStringDataItem, { "StringData", 's' } },
67 { DexFile::kDexTypeDebugInfoItem, { "DebugInfo", 'D' } },
68 { DexFile::kDexTypeAnnotationItem, { "AnnotationItem", '3' } },
69 { DexFile::kDexTypeEncodedArrayItem, { "EncodedArrayItem", 'E' } },
70 { DexFile::kDexTypeAnnotationsDirectoryItem, { "AnnotationsDirectoryItem", '4' } }
71};
72
73class PageCount {
74 public:
75 PageCount() {
76 for (auto it = kDexSectionInfoMap.begin(); it != kDexSectionInfoMap.end(); ++it) {
77 map_[it->first] = 0;
78 }
79 }
80 void Increment(uint16_t type) {
81 map_[type]++;
82 }
83 size_t Get(uint16_t type) const {
84 return map_.at(type);
85 }
86 private:
87 std::map<uint16_t, size_t> map_;
88 DISALLOW_COPY_AND_ASSIGN(PageCount);
89};
90
91static void PrintLetterKey() {
92 std::cout << "letter section_type" << std::endl;
93 for (const auto& p : kDexSectionInfoMap) {
94 const DexSectionInfo& section_info = p.second;
95 std::cout << section_info.letter << " " << section_info.name.c_str() << std::endl;
96 }
97}
98
99static char PageTypeChar(uint16_t type) {
100 if (kDexSectionInfoMap.find(type) == kDexSectionInfoMap.end()) {
101 return '-';
102 }
103 return kDexSectionInfoMap.find(type)->second.letter;
104}
105
106static uint16_t FindSectionTypeForPage(size_t page,
107 const std::vector<dex_ir::DexFileSection>& sections) {
108 for (const auto& section : sections) {
109 size_t first_page_of_section = section.offset / kPageSize;
110 // Only consider non-empty sections.
111 if (section.size == 0) {
112 continue;
113 }
114 // Attribute the page to the highest-offset section that starts before the page.
115 if (first_page_of_section <= page) {
116 return section.type;
117 }
118 }
119 // If there's no non-zero sized section with an offset below offset we're looking for, it
120 // must be the header.
121 return DexFile::kDexTypeHeaderItem;
122}
123
124static void ProcessPageMap(uint64_t* pagemap,
125 size_t start,
126 size_t end,
127 const std::vector<dex_ir::DexFileSection>& sections,
128 PageCount* page_counts) {
129 for (size_t page = start; page < end; ++page) {
130 char type_char = '.';
131 if (PM_PAGEMAP_PRESENT(pagemap[page])) {
132 uint16_t type = FindSectionTypeForPage(page, sections);
133 page_counts->Increment(type);
134 type_char = PageTypeChar(type);
135 }
136 if (g_verbose) {
137 std::cout << type_char;
138 if ((page - start) % kLineLength == kLineLength - 1) {
139 std::cout << std::endl;
140 }
141 }
142 }
143 if (g_verbose) {
144 if ((end - start) % kLineLength != 0) {
145 std::cout << std::endl;
146 }
147 }
148}
149
150static void DisplayDexStatistics(size_t start,
151 size_t end,
152 const PageCount& resident_pages,
153 const std::vector<dex_ir::DexFileSection>& sections) {
154 // Compute the total possible sizes for sections.
155 PageCount mapped_pages;
156 DCHECK_GE(end, start);
157 size_t total_mapped_pages = end - start;
158 if (total_mapped_pages == 0) {
159 return;
160 }
161 for (size_t page = start; page < end; ++page) {
162 mapped_pages.Increment(FindSectionTypeForPage(page, sections));
163 }
164 size_t total_resident_pages = 0;
165 // Compute the width of the section header column in the table (for fixed formatting).
166 int section_header_width = 0;
167 for (const auto& section_info : kDexSectionInfoMap) {
168 section_header_width = std::max(section_header_width,
169 static_cast<int>(section_info.second.name.length()));
170 }
171 // The width needed to print a file page offset (32-bit).
172 static constexpr int kPageCountWidth =
173 static_cast<int>(std::numeric_limits<uint32_t>::digits10);
174 // Display the sections.
175 static constexpr char kSectionHeader[] = "Section name";
176 std::cout << StringPrintf("%-*s %*s %*s %% of %% of",
177 section_header_width,
178 kSectionHeader,
179 kPageCountWidth,
180 "resident",
181 kPageCountWidth,
182 "total"
183 )
184 << std::endl;
185 std::cout << StringPrintf("%-*s %*s %*s sect. total",
186 section_header_width,
187 "",
188 kPageCountWidth,
189 "pages",
190 kPageCountWidth,
191 "pages")
192 << std::endl;
193 for (size_t i = sections.size(); i > 0; --i) {
194 const dex_ir::DexFileSection& section = sections[i - 1];
195 const uint16_t type = section.type;
196 const DexSectionInfo& section_info = kDexSectionInfoMap.find(type)->second;
197 size_t pages_resident = resident_pages.Get(type);
198 double percent_resident = 0;
199 if (mapped_pages.Get(type) > 0) {
200 percent_resident = 100.0 * pages_resident / mapped_pages.Get(type);
201 }
202 // 6.2 is sufficient to print 0-100% with two decimal places of accuracy.
203 std::cout << StringPrintf("%-*s %*zd %*zd %6.2f %6.2f",
204 section_header_width,
205 section_info.name.c_str(),
206 kPageCountWidth,
207 pages_resident,
208 kPageCountWidth,
209 mapped_pages.Get(type),
210 percent_resident,
211 100.0 * pages_resident / total_mapped_pages)
212 << std::endl;
213 total_resident_pages += pages_resident;
214 }
215 std::cout << StringPrintf("%-*s %*zd %*zd %6.2f",
216 section_header_width,
217 "GRAND TOTAL",
218 kPageCountWidth,
219 total_resident_pages,
220 kPageCountWidth,
221 total_mapped_pages,
222 100.0 * total_resident_pages / total_mapped_pages)
223 << std::endl
224 << std::endl;
225}
226
227static void ProcessOneDexMapping(uint64_t* pagemap,
228 uint64_t map_start,
229 const DexFile* dex_file,
230 uint64_t vdex_start) {
231 uint64_t dex_file_start = reinterpret_cast<uint64_t>(dex_file->Begin());
232 size_t dex_file_size = dex_file->Size();
233 if (dex_file_start < vdex_start) {
234 std::cerr << "Dex file start offset for "
235 << dex_file->GetLocation().c_str()
236 << " is incorrect: map start "
237 << StringPrintf("%zx > dex start %zx\n", map_start, dex_file_start)
238 << std::endl;
239 return;
240 }
241 uint64_t start = (dex_file_start - vdex_start) / kPageSize;
242 uint64_t end = RoundUp(start + dex_file_size, kPageSize) / kPageSize;
243 std::cout << "DEX "
244 << dex_file->GetLocation().c_str()
245 << StringPrintf(": %zx-%zx",
246 map_start + start * kPageSize,
247 map_start + end * kPageSize)
248 << std::endl;
249 // Build a list of the dex file section types, sorted from highest offset to lowest.
250 std::vector<dex_ir::DexFileSection> sections;
251 {
252 std::unique_ptr<dex_ir::Header> header(dex_ir::DexIrBuilder(*dex_file));
253 sections = dex_ir::GetSortedDexFileSections(header.get(),
254 dex_ir::SortDirection::kSortDescending);
255 }
256 PageCount section_resident_pages;
257 ProcessPageMap(pagemap, start, end, sections, &section_resident_pages);
258 if (g_show_statistics) {
259 DisplayDexStatistics(start, end, section_resident_pages, sections);
260 }
261}
262
263static bool DisplayMappingIfFromVdexFile(pm_map_t* map) {
264 // Confirm that the map is from a vdex file.
265 static const char* suffixes[] = { ".vdex" };
266 std::string vdex_name;
267 bool found = false;
268 for (size_t j = 0; j < sizeof(suffixes) / sizeof(suffixes[0]); ++j) {
269 if (strstr(pm_map_name(map), suffixes[j]) != nullptr) {
270 vdex_name = pm_map_name(map);
271 found = true;
272 break;
273 }
274 }
275 if (!found) {
276 return true;
277 }
278 // Extract all the dex files from the vdex file.
279 std::string error_msg;
280 std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_name,
281 false /*writeable*/,
282 false /*low_4gb*/,
283 &error_msg /*out*/));
284 if (vdex == nullptr) {
285 std::cerr << "Could not open vdex file "
286 << vdex_name.c_str()
287 << ": error "
288 << error_msg.c_str()
289 << std::endl;
290 return false;
291 }
292
293 std::vector<std::unique_ptr<const DexFile>> dex_files;
294 if (!vdex->OpenAllDexFiles(&dex_files, &error_msg)) {
295 std::cerr << "Dex files could not be opened for "
296 << vdex_name.c_str()
297 << ": error "
298 << error_msg.c_str()
299 << std::endl;
300 }
301 // Open the page mapping (one uint64_t per page) for the entire vdex mapping.
302 uint64_t* pagemap;
303 size_t len;
304 if (pm_map_pagemap(map, &pagemap, &len) != 0) {
305 std::cerr << "Error creating pagemap." << std::endl;
306 return false;
307 }
308 // Process the dex files.
309 std::cout << "MAPPING "
310 << pm_map_name(map)
311 << StringPrintf(": %zx-%zx", pm_map_start(map), pm_map_end(map))
312 << std::endl;
313 for (const auto& dex_file : dex_files) {
314 ProcessOneDexMapping(pagemap,
315 pm_map_start(map),
316 dex_file.get(),
317 reinterpret_cast<uint64_t>(vdex->Begin()));
318 }
319 free(pagemap);
320 return true;
321}
322
323
324static void Usage(const char* cmd) {
325 std::cerr << "Usage: " << cmd << " [-k] [-s] [-v] pid" << std::endl
326 << " -k Shows a key to verbose display characters." << std::endl
327 << " -s Shows section statistics for individual dex files." << std::endl
328 << " -v Verbosely displays resident pages for dex files." << std::endl;
329}
330
331static int DexDiagMain(int argc, char* argv[]) {
332 if (argc < 2) {
333 Usage(argv[0]);
334 return EXIT_FAILURE;
335 }
336
337 // TODO: add option to track usage by class name, etc.
338 for (int i = 1; i < argc - 1; ++i) {
339 if (strcmp(argv[i], "-k") == 0) {
340 g_show_key = true;
341 } else if (strcmp(argv[i], "-s") == 0) {
342 g_show_statistics = true;
343 } else if (strcmp(argv[i], "-v") == 0) {
344 g_verbose = true;
345 } else {
346 Usage(argv[0]);
347 return EXIT_FAILURE;
348 }
349 }
350
351 // Art specific set up.
352 InitLogging(argv, Runtime::Aborter);
353 MemMap::Init();
354
355 pid_t pid;
356 char* endptr;
357 pid = (pid_t)strtol(argv[argc - 1], &endptr, 10);
358 if (*endptr != '\0' || kill(pid, 0) != 0) {
359 std::cerr << StringPrintf("Invalid PID \"%s\".\n", argv[argc - 1]) << std::endl;
360 return EXIT_FAILURE;
361 }
362
363 // get libpagemap kernel information.
364 pm_kernel_t* ker;
365 if (pm_kernel_create(&ker) != 0) {
366 std::cerr << "Error creating kernel interface -- does this kernel have pagemap?" << std::endl;
367 return EXIT_FAILURE;
368 }
369
370 // get libpagemap process information.
371 pm_process_t* proc;
372 if (pm_process_create(ker, pid, &proc) != 0) {
373 std::cerr << "Error creating process interface -- does process "
374 << pid
375 << " really exist?"
376 << std::endl;
377 return EXIT_FAILURE;
378 }
379
380 // Get the set of mappings by the specified process.
381 pm_map_t** maps;
382 size_t num_maps;
383 if (pm_process_maps(proc, &maps, &num_maps) != 0) {
384 std::cerr << "Error listing maps." << std::endl;
385 return EXIT_FAILURE;
386 }
387
388 // Process the mappings that are due to DEX files.
389 for (size_t i = 0; i < num_maps; ++i) {
390 if (!DisplayMappingIfFromVdexFile(maps[i])) {
391 return EXIT_FAILURE;
392 }
393 }
394
395 if (g_show_key) {
396 PrintLetterKey();
397 }
398 return 0;
399}
400
401} // namespace art
402
403int main(int argc, char* argv[]) {
404 return art::DexDiagMain(argc, argv);
405}