xref: /aosp_15_r20/art/imgdiag/page_info.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2023 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  */
16*795d594fSAndroid Build Coastguard Worker 
17*795d594fSAndroid Build Coastguard Worker #include <signal.h>
18*795d594fSAndroid Build Coastguard Worker #include <stdio.h>
19*795d594fSAndroid Build Coastguard Worker #include <stdlib.h>
20*795d594fSAndroid Build Coastguard Worker #include <sys/stat.h>
21*795d594fSAndroid Build Coastguard Worker #include <sys/types.h>
22*795d594fSAndroid Build Coastguard Worker 
23*795d594fSAndroid Build Coastguard Worker #include <functional>
24*795d594fSAndroid Build Coastguard Worker #include <optional>
25*795d594fSAndroid Build Coastguard Worker #include <ostream>
26*795d594fSAndroid Build Coastguard Worker #include <string>
27*795d594fSAndroid Build Coastguard Worker #include <vector>
28*795d594fSAndroid Build Coastguard Worker 
29*795d594fSAndroid Build Coastguard Worker #include "android-base/parseint.h"
30*795d594fSAndroid Build Coastguard Worker #include "android-base/stringprintf.h"
31*795d594fSAndroid Build Coastguard Worker #include "base/os.h"
32*795d594fSAndroid Build Coastguard Worker #include "base/unix_file/fd_file.h"
33*795d594fSAndroid Build Coastguard Worker #include "cmdline.h"
34*795d594fSAndroid Build Coastguard Worker #include "page_util.h"
35*795d594fSAndroid Build Coastguard Worker #include "procinfo/process_map.h"
36*795d594fSAndroid Build Coastguard Worker #include "scoped_thread_state_change-inl.h"
37*795d594fSAndroid Build Coastguard Worker 
38*795d594fSAndroid Build Coastguard Worker namespace art {
39*795d594fSAndroid Build Coastguard Worker 
40*795d594fSAndroid Build Coastguard Worker using android::base::StringPrintf;
41*795d594fSAndroid Build Coastguard Worker 
42*795d594fSAndroid Build Coastguard Worker namespace {
43*795d594fSAndroid Build Coastguard Worker 
44*795d594fSAndroid Build Coastguard Worker struct ProcFiles {
45*795d594fSAndroid Build Coastguard Worker   // A File for reading /proc/<pid>/mem.
46*795d594fSAndroid Build Coastguard Worker   File mem;
47*795d594fSAndroid Build Coastguard Worker   // A File for reading /proc/<pid>/pagemap.
48*795d594fSAndroid Build Coastguard Worker   File pagemap;
49*795d594fSAndroid Build Coastguard Worker   // A File for reading /proc/kpageflags.
50*795d594fSAndroid Build Coastguard Worker   File kpageflags;
51*795d594fSAndroid Build Coastguard Worker   // A File for reading /proc/kpagecount.
52*795d594fSAndroid Build Coastguard Worker   File kpagecount;
53*795d594fSAndroid Build Coastguard Worker };
54*795d594fSAndroid Build Coastguard Worker 
OpenFile(const char * file_name,File & file,std::string & error_msg)55*795d594fSAndroid Build Coastguard Worker bool OpenFile(const char* file_name, /*out*/ File& file, /*out*/ std::string& error_msg) {
56*795d594fSAndroid Build Coastguard Worker   std::unique_ptr<File> file_ptr = std::unique_ptr<File>{OS::OpenFileForReading(file_name)};
57*795d594fSAndroid Build Coastguard Worker   if (file_ptr == nullptr) {
58*795d594fSAndroid Build Coastguard Worker     error_msg = StringPrintf("Failed to open file: %s", file_name);
59*795d594fSAndroid Build Coastguard Worker     return false;
60*795d594fSAndroid Build Coastguard Worker   }
61*795d594fSAndroid Build Coastguard Worker   file = std::move(*file_ptr);
62*795d594fSAndroid Build Coastguard Worker   return true;
63*795d594fSAndroid Build Coastguard Worker }
64*795d594fSAndroid Build Coastguard Worker 
OpenProcFiles(pid_t pid,ProcFiles & files,std::string & error_msg)65*795d594fSAndroid Build Coastguard Worker bool OpenProcFiles(pid_t pid, /*out*/ ProcFiles& files, /*out*/ std::string& error_msg) {
66*795d594fSAndroid Build Coastguard Worker   if (!OpenFile("/proc/kpageflags", files.kpageflags, error_msg)) {
67*795d594fSAndroid Build Coastguard Worker     return false;
68*795d594fSAndroid Build Coastguard Worker   }
69*795d594fSAndroid Build Coastguard Worker   if (!OpenFile("/proc/kpagecount", files.kpagecount, error_msg)) {
70*795d594fSAndroid Build Coastguard Worker     return false;
71*795d594fSAndroid Build Coastguard Worker   }
72*795d594fSAndroid Build Coastguard Worker   std::string mem_file_name =
73*795d594fSAndroid Build Coastguard Worker       StringPrintf("/proc/%ld/mem", static_cast<long>(pid));  // NOLINT [runtime/int]
74*795d594fSAndroid Build Coastguard Worker   if (!OpenFile(mem_file_name.c_str(), files.mem, error_msg)) {
75*795d594fSAndroid Build Coastguard Worker     return false;
76*795d594fSAndroid Build Coastguard Worker   }
77*795d594fSAndroid Build Coastguard Worker   std::string pagemap_file_name =
78*795d594fSAndroid Build Coastguard Worker       StringPrintf("/proc/%ld/pagemap", static_cast<long>(pid));  // NOLINT [runtime/int]
79*795d594fSAndroid Build Coastguard Worker   if (!OpenFile(pagemap_file_name.c_str(), files.pagemap, error_msg)) {
80*795d594fSAndroid Build Coastguard Worker     return false;
81*795d594fSAndroid Build Coastguard Worker   }
82*795d594fSAndroid Build Coastguard Worker   return true;
83*795d594fSAndroid Build Coastguard Worker }
84*795d594fSAndroid Build Coastguard Worker 
DumpPageInfo(uint64_t virtual_page_index,ProcFiles & proc_files,std::ostream & os,size_t page_size)85*795d594fSAndroid Build Coastguard Worker void DumpPageInfo(uint64_t virtual_page_index, ProcFiles& proc_files, std::ostream& os,
86*795d594fSAndroid Build Coastguard Worker                   size_t page_size) {
87*795d594fSAndroid Build Coastguard Worker   const uint64_t virtual_page_addr = virtual_page_index * page_size;
88*795d594fSAndroid Build Coastguard Worker   os << "Virtual page index: " << virtual_page_index << "\n";
89*795d594fSAndroid Build Coastguard Worker   os << "Virtual page addr: " << virtual_page_addr << "\n";
90*795d594fSAndroid Build Coastguard Worker 
91*795d594fSAndroid Build Coastguard Worker   std::string error_msg;
92*795d594fSAndroid Build Coastguard Worker   uint64_t page_frame_number = -1;
93*795d594fSAndroid Build Coastguard Worker   if (!GetPageFrameNumber(
94*795d594fSAndroid Build Coastguard Worker           proc_files.pagemap, virtual_page_index, /*out*/ page_frame_number, /*out*/ error_msg)) {
95*795d594fSAndroid Build Coastguard Worker     os << "Failed to get page frame number: " << error_msg << "\n";
96*795d594fSAndroid Build Coastguard Worker     return;
97*795d594fSAndroid Build Coastguard Worker   }
98*795d594fSAndroid Build Coastguard Worker   os << "Page frame number: " << page_frame_number << "\n";
99*795d594fSAndroid Build Coastguard Worker 
100*795d594fSAndroid Build Coastguard Worker   uint64_t page_count = -1;
101*795d594fSAndroid Build Coastguard Worker   if (!GetPageFlagsOrCount(proc_files.kpagecount,
102*795d594fSAndroid Build Coastguard Worker                            page_frame_number,
103*795d594fSAndroid Build Coastguard Worker                            /*out*/ page_count,
104*795d594fSAndroid Build Coastguard Worker                            /*out*/ error_msg)) {
105*795d594fSAndroid Build Coastguard Worker     os << "Failed to get page count: " << error_msg << "\n";
106*795d594fSAndroid Build Coastguard Worker     return;
107*795d594fSAndroid Build Coastguard Worker   }
108*795d594fSAndroid Build Coastguard Worker   os << "kpagecount: " << page_count << "\n";
109*795d594fSAndroid Build Coastguard Worker 
110*795d594fSAndroid Build Coastguard Worker   uint64_t page_flags = 0;
111*795d594fSAndroid Build Coastguard Worker   if (!GetPageFlagsOrCount(proc_files.kpageflags,
112*795d594fSAndroid Build Coastguard Worker                            page_frame_number,
113*795d594fSAndroid Build Coastguard Worker                            /*out*/ page_flags,
114*795d594fSAndroid Build Coastguard Worker                            /*out*/ error_msg)) {
115*795d594fSAndroid Build Coastguard Worker     os << "Failed to get page flags: " << error_msg << "\n";
116*795d594fSAndroid Build Coastguard Worker     return;
117*795d594fSAndroid Build Coastguard Worker   }
118*795d594fSAndroid Build Coastguard Worker   os << "kpageflags: " << page_flags << "\n";
119*795d594fSAndroid Build Coastguard Worker 
120*795d594fSAndroid Build Coastguard Worker   if (page_count != 0) {
121*795d594fSAndroid Build Coastguard Worker     std::vector<uint8_t> page_contents(page_size);
122*795d594fSAndroid Build Coastguard Worker     if (!proc_files.mem.PreadFully(page_contents.data(), page_contents.size(), virtual_page_addr)) {
123*795d594fSAndroid Build Coastguard Worker       os << "Failed to read page contents\n";
124*795d594fSAndroid Build Coastguard Worker       return;
125*795d594fSAndroid Build Coastguard Worker     }
126*795d594fSAndroid Build Coastguard Worker     os << "Zero bytes: " << std::count(std::begin(page_contents), std::end(page_contents), 0)
127*795d594fSAndroid Build Coastguard Worker        << "\n";
128*795d594fSAndroid Build Coastguard Worker   }
129*795d594fSAndroid Build Coastguard Worker }
130*795d594fSAndroid Build Coastguard Worker 
131*795d594fSAndroid Build Coastguard Worker struct MapPageCounts {
132*795d594fSAndroid Build Coastguard Worker   // Present pages count.
133*795d594fSAndroid Build Coastguard Worker   uint64_t pages = 0;
134*795d594fSAndroid Build Coastguard Worker   // Non-present pages count.
135*795d594fSAndroid Build Coastguard Worker   uint64_t non_present_pages = 0;
136*795d594fSAndroid Build Coastguard Worker   // Private (kpagecount == 1) zero page count.
137*795d594fSAndroid Build Coastguard Worker   uint64_t private_zero_pages = 0;
138*795d594fSAndroid Build Coastguard Worker   // Shared (kpagecount > 1) zero page count.
139*795d594fSAndroid Build Coastguard Worker   uint64_t shared_zero_pages = 0;
140*795d594fSAndroid Build Coastguard Worker   // Physical frame numbers of zero pages.
141*795d594fSAndroid Build Coastguard Worker   std::unordered_set<uint64_t> zero_page_pfns;
142*795d594fSAndroid Build Coastguard Worker 
143*795d594fSAndroid Build Coastguard Worker   // Memory map name.
144*795d594fSAndroid Build Coastguard Worker   std::string name;
145*795d594fSAndroid Build Coastguard Worker   // Memory map start address.
146*795d594fSAndroid Build Coastguard Worker   uint64_t start = 0;
147*795d594fSAndroid Build Coastguard Worker   // Memory map end address.
148*795d594fSAndroid Build Coastguard Worker   uint64_t end = 0;
149*795d594fSAndroid Build Coastguard Worker };
150*795d594fSAndroid Build Coastguard Worker 
GetMapPageCounts(ProcFiles & proc_files,const android::procinfo::MapInfo & map_info,MapPageCounts & map_page_counts,std::string & error_msg,size_t page_size)151*795d594fSAndroid Build Coastguard Worker bool GetMapPageCounts(ProcFiles& proc_files,
152*795d594fSAndroid Build Coastguard Worker                       const android::procinfo::MapInfo& map_info,
153*795d594fSAndroid Build Coastguard Worker                       MapPageCounts& map_page_counts,
154*795d594fSAndroid Build Coastguard Worker                       std::string& error_msg,
155*795d594fSAndroid Build Coastguard Worker                       size_t page_size) {
156*795d594fSAndroid Build Coastguard Worker   map_page_counts.name = map_info.name;
157*795d594fSAndroid Build Coastguard Worker   map_page_counts.start = map_info.start;
158*795d594fSAndroid Build Coastguard Worker   map_page_counts.end = map_info.end;
159*795d594fSAndroid Build Coastguard Worker   std::vector<uint8_t> page_contents(page_size);
160*795d594fSAndroid Build Coastguard Worker   for (uint64_t begin = map_info.start; begin < map_info.end; begin += page_size) {
161*795d594fSAndroid Build Coastguard Worker     const size_t virtual_page_index = begin / page_size;
162*795d594fSAndroid Build Coastguard Worker     uint64_t page_frame_number = -1;
163*795d594fSAndroid Build Coastguard Worker     if (!GetPageFrameNumber(proc_files.pagemap, virtual_page_index, page_frame_number, error_msg)) {
164*795d594fSAndroid Build Coastguard Worker       return false;
165*795d594fSAndroid Build Coastguard Worker     }
166*795d594fSAndroid Build Coastguard Worker     uint64_t page_count = -1;
167*795d594fSAndroid Build Coastguard Worker     if (!GetPageFlagsOrCounts(proc_files.kpagecount,
168*795d594fSAndroid Build Coastguard Worker                               ArrayRef<const uint64_t>(&page_frame_number, 1),
169*795d594fSAndroid Build Coastguard Worker                               /*out*/ ArrayRef<uint64_t>(&page_count, 1),
170*795d594fSAndroid Build Coastguard Worker                               /*out*/ error_msg)) {
171*795d594fSAndroid Build Coastguard Worker       return false;
172*795d594fSAndroid Build Coastguard Worker     }
173*795d594fSAndroid Build Coastguard Worker 
174*795d594fSAndroid Build Coastguard Worker     const auto is_zero_page = [](const std::vector<uint8_t>& page) {
175*795d594fSAndroid Build Coastguard Worker       const auto non_zero_it =
176*795d594fSAndroid Build Coastguard Worker           std::find_if(std::begin(page), std::end(page), [](uint8_t b) { return b != 0; });
177*795d594fSAndroid Build Coastguard Worker       return non_zero_it == std::end(page);
178*795d594fSAndroid Build Coastguard Worker     };
179*795d594fSAndroid Build Coastguard Worker 
180*795d594fSAndroid Build Coastguard Worker     if (page_count == 0) {
181*795d594fSAndroid Build Coastguard Worker       map_page_counts.non_present_pages += 1;
182*795d594fSAndroid Build Coastguard Worker       continue;
183*795d594fSAndroid Build Coastguard Worker     }
184*795d594fSAndroid Build Coastguard Worker 
185*795d594fSAndroid Build Coastguard Worker     // Handle present page.
186*795d594fSAndroid Build Coastguard Worker     if (!proc_files.mem.PreadFully(page_contents.data(), page_contents.size(), begin)) {
187*795d594fSAndroid Build Coastguard Worker       error_msg = StringPrintf(
188*795d594fSAndroid Build Coastguard Worker           "Failed to read present page %" PRIx64 " for mapping %s\n", begin, map_info.name.c_str());
189*795d594fSAndroid Build Coastguard Worker       return false;
190*795d594fSAndroid Build Coastguard Worker     }
191*795d594fSAndroid Build Coastguard Worker     const bool is_zero = is_zero_page(page_contents);
192*795d594fSAndroid Build Coastguard Worker     const bool is_private = (page_count == 1);
193*795d594fSAndroid Build Coastguard Worker     map_page_counts.pages += 1;
194*795d594fSAndroid Build Coastguard Worker     if (is_zero) {
195*795d594fSAndroid Build Coastguard Worker       map_page_counts.zero_page_pfns.insert(page_frame_number);
196*795d594fSAndroid Build Coastguard Worker       if (is_private) {
197*795d594fSAndroid Build Coastguard Worker         map_page_counts.private_zero_pages += 1;
198*795d594fSAndroid Build Coastguard Worker       } else {
199*795d594fSAndroid Build Coastguard Worker         map_page_counts.shared_zero_pages += 1;
200*795d594fSAndroid Build Coastguard Worker       }
201*795d594fSAndroid Build Coastguard Worker     }
202*795d594fSAndroid Build Coastguard Worker   }
203*795d594fSAndroid Build Coastguard Worker   return true;
204*795d594fSAndroid Build Coastguard Worker }
205*795d594fSAndroid Build Coastguard Worker 
CountZeroPages(pid_t pid,ProcFiles & proc_files,std::ostream & os,size_t page_size)206*795d594fSAndroid Build Coastguard Worker void CountZeroPages(pid_t pid, ProcFiles& proc_files, std::ostream& os, size_t page_size) {
207*795d594fSAndroid Build Coastguard Worker   std::vector<android::procinfo::MapInfo> proc_maps;
208*795d594fSAndroid Build Coastguard Worker   if (!android::procinfo::ReadProcessMaps(pid, &proc_maps)) {
209*795d594fSAndroid Build Coastguard Worker     os << "Could not read process maps for " << pid;
210*795d594fSAndroid Build Coastguard Worker     return;
211*795d594fSAndroid Build Coastguard Worker   }
212*795d594fSAndroid Build Coastguard Worker 
213*795d594fSAndroid Build Coastguard Worker   MapPageCounts total;
214*795d594fSAndroid Build Coastguard Worker   std::vector<MapPageCounts> stats;
215*795d594fSAndroid Build Coastguard Worker   for (const android::procinfo::MapInfo& map_info : proc_maps) {
216*795d594fSAndroid Build Coastguard Worker     MapPageCounts map_page_counts;
217*795d594fSAndroid Build Coastguard Worker     std::string error_msg;
218*795d594fSAndroid Build Coastguard Worker     if (!GetMapPageCounts(proc_files, map_info, map_page_counts, error_msg, page_size)) {
219*795d594fSAndroid Build Coastguard Worker       os << "Error getting map page counts for: " << map_info.name << "\n" << error_msg << "\n\n";
220*795d594fSAndroid Build Coastguard Worker       continue;
221*795d594fSAndroid Build Coastguard Worker     }
222*795d594fSAndroid Build Coastguard Worker     total.pages += map_page_counts.pages;
223*795d594fSAndroid Build Coastguard Worker     total.private_zero_pages += map_page_counts.private_zero_pages;
224*795d594fSAndroid Build Coastguard Worker     total.shared_zero_pages += map_page_counts.shared_zero_pages;
225*795d594fSAndroid Build Coastguard Worker     total.non_present_pages += map_page_counts.non_present_pages;
226*795d594fSAndroid Build Coastguard Worker     total.zero_page_pfns.insert(std::begin(map_page_counts.zero_page_pfns),
227*795d594fSAndroid Build Coastguard Worker                                 std::end(map_page_counts.zero_page_pfns));
228*795d594fSAndroid Build Coastguard Worker     stats.push_back(std::move(map_page_counts));
229*795d594fSAndroid Build Coastguard Worker   }
230*795d594fSAndroid Build Coastguard Worker 
231*795d594fSAndroid Build Coastguard Worker   // Sort by different page counts, descending.
232*795d594fSAndroid Build Coastguard Worker   const auto sort_by_private_zero_pages = [](const auto& stats1, const auto& stats2) {
233*795d594fSAndroid Build Coastguard Worker     return stats1.private_zero_pages > stats2.private_zero_pages;
234*795d594fSAndroid Build Coastguard Worker   };
235*795d594fSAndroid Build Coastguard Worker   const auto sort_by_shared_zero_pages = [](const auto& stats1, const auto& stats2) {
236*795d594fSAndroid Build Coastguard Worker     return stats1.shared_zero_pages > stats2.shared_zero_pages;
237*795d594fSAndroid Build Coastguard Worker   };
238*795d594fSAndroid Build Coastguard Worker   const auto sort_by_unique_zero_pages = [](const auto& stats1, const auto& stats2) {
239*795d594fSAndroid Build Coastguard Worker     return stats1.zero_page_pfns.size() > stats2.zero_page_pfns.size();
240*795d594fSAndroid Build Coastguard Worker   };
241*795d594fSAndroid Build Coastguard Worker 
242*795d594fSAndroid Build Coastguard Worker   // Print up to `max_lines` entries.
243*795d594fSAndroid Build Coastguard Worker   const auto print_stats = [&stats, &os](size_t max_lines) {
244*795d594fSAndroid Build Coastguard Worker     for (const MapPageCounts& map_page_counts : stats) {
245*795d594fSAndroid Build Coastguard Worker       if (max_lines == 0) {
246*795d594fSAndroid Build Coastguard Worker         return;
247*795d594fSAndroid Build Coastguard Worker       }
248*795d594fSAndroid Build Coastguard Worker       // Skip entries with no present pages.
249*795d594fSAndroid Build Coastguard Worker       if (map_page_counts.pages == 0) {
250*795d594fSAndroid Build Coastguard Worker         continue;
251*795d594fSAndroid Build Coastguard Worker       }
252*795d594fSAndroid Build Coastguard Worker       max_lines -= 1;
253*795d594fSAndroid Build Coastguard Worker       os << StringPrintf("%" PRIx64 "-%" PRIx64 " %s: pages=%" PRIu64
254*795d594fSAndroid Build Coastguard Worker                          ", private_zero_pages=%" PRIu64 ", shared_zero_pages=%" PRIu64
255*795d594fSAndroid Build Coastguard Worker                          ", unique_zero_pages=%" PRIu64 ", non_present_pages=%" PRIu64 "\n",
256*795d594fSAndroid Build Coastguard Worker                          map_page_counts.start,
257*795d594fSAndroid Build Coastguard Worker                          map_page_counts.end,
258*795d594fSAndroid Build Coastguard Worker                          map_page_counts.name.c_str(),
259*795d594fSAndroid Build Coastguard Worker                          map_page_counts.pages,
260*795d594fSAndroid Build Coastguard Worker                          map_page_counts.private_zero_pages,
261*795d594fSAndroid Build Coastguard Worker                          map_page_counts.shared_zero_pages,
262*795d594fSAndroid Build Coastguard Worker                          uint64_t{map_page_counts.zero_page_pfns.size()},
263*795d594fSAndroid Build Coastguard Worker                          map_page_counts.non_present_pages);
264*795d594fSAndroid Build Coastguard Worker     }
265*795d594fSAndroid Build Coastguard Worker   };
266*795d594fSAndroid Build Coastguard Worker 
267*795d594fSAndroid Build Coastguard Worker   os << StringPrintf("total_pages=%" PRIu64 ", total_private_zero_pages=%" PRIu64
268*795d594fSAndroid Build Coastguard Worker                      ", total_shared_zero_pages=%" PRIu64 ", total_unique_zero_pages=%" PRIu64
269*795d594fSAndroid Build Coastguard Worker                      ", total_non_present_pages=%" PRIu64 "\n",
270*795d594fSAndroid Build Coastguard Worker                      total.pages,
271*795d594fSAndroid Build Coastguard Worker                      total.private_zero_pages,
272*795d594fSAndroid Build Coastguard Worker                      total.shared_zero_pages,
273*795d594fSAndroid Build Coastguard Worker                      uint64_t{total.zero_page_pfns.size()},
274*795d594fSAndroid Build Coastguard Worker                      total.non_present_pages);
275*795d594fSAndroid Build Coastguard Worker   os << "\n\n";
276*795d594fSAndroid Build Coastguard Worker 
277*795d594fSAndroid Build Coastguard Worker   const size_t top_lines = std::min(size_t{20}, stats.size());
278*795d594fSAndroid Build Coastguard Worker   std::partial_sort(
279*795d594fSAndroid Build Coastguard Worker       std::begin(stats), std::begin(stats) + top_lines, std::end(stats), sort_by_unique_zero_pages);
280*795d594fSAndroid Build Coastguard Worker   os << "Top " << top_lines << " maps by unique zero pages (unique PFN count)\n";
281*795d594fSAndroid Build Coastguard Worker   print_stats(top_lines);
282*795d594fSAndroid Build Coastguard Worker   os << "\n\n";
283*795d594fSAndroid Build Coastguard Worker 
284*795d594fSAndroid Build Coastguard Worker   std::partial_sort(std::begin(stats),
285*795d594fSAndroid Build Coastguard Worker                     std::begin(stats) + top_lines,
286*795d594fSAndroid Build Coastguard Worker                     std::end(stats),
287*795d594fSAndroid Build Coastguard Worker                     sort_by_private_zero_pages);
288*795d594fSAndroid Build Coastguard Worker   os << "Top " << top_lines << " maps by private zero pages (kpagecount == 1)\n";
289*795d594fSAndroid Build Coastguard Worker   print_stats(top_lines);
290*795d594fSAndroid Build Coastguard Worker   os << "\n\n";
291*795d594fSAndroid Build Coastguard Worker 
292*795d594fSAndroid Build Coastguard Worker   std::partial_sort(
293*795d594fSAndroid Build Coastguard Worker       std::begin(stats), std::begin(stats) + top_lines, std::end(stats), sort_by_shared_zero_pages);
294*795d594fSAndroid Build Coastguard Worker   os << "Top " << top_lines << " maps by shared zero pages (kpagecount > 1)\n";
295*795d594fSAndroid Build Coastguard Worker   print_stats(top_lines);
296*795d594fSAndroid Build Coastguard Worker   os << "\n\n";
297*795d594fSAndroid Build Coastguard Worker 
298*795d594fSAndroid Build Coastguard Worker   std::sort(std::begin(stats), std::end(stats), sort_by_unique_zero_pages);
299*795d594fSAndroid Build Coastguard Worker   os << "All maps by unique zero pages (unique PFN count)\n";
300*795d594fSAndroid Build Coastguard Worker   print_stats(stats.size());
301*795d594fSAndroid Build Coastguard Worker   os << "\n\n";
302*795d594fSAndroid Build Coastguard Worker }
303*795d594fSAndroid Build Coastguard Worker 
304*795d594fSAndroid Build Coastguard Worker }  // namespace
305*795d594fSAndroid Build Coastguard Worker 
PageInfo(std::ostream & os,pid_t pid,bool count_zero_pages,std::optional<uint64_t> virtual_page_index,size_t page_size)306*795d594fSAndroid Build Coastguard Worker int PageInfo(std::ostream& os,
307*795d594fSAndroid Build Coastguard Worker              pid_t pid,
308*795d594fSAndroid Build Coastguard Worker              bool count_zero_pages,
309*795d594fSAndroid Build Coastguard Worker              std::optional<uint64_t> virtual_page_index,
310*795d594fSAndroid Build Coastguard Worker              size_t page_size) {
311*795d594fSAndroid Build Coastguard Worker   ProcFiles proc_files;
312*795d594fSAndroid Build Coastguard Worker   std::string error_msg;
313*795d594fSAndroid Build Coastguard Worker   if (!OpenProcFiles(pid, proc_files, error_msg)) {
314*795d594fSAndroid Build Coastguard Worker     os << error_msg;
315*795d594fSAndroid Build Coastguard Worker     return EXIT_FAILURE;
316*795d594fSAndroid Build Coastguard Worker   }
317*795d594fSAndroid Build Coastguard Worker   if (virtual_page_index != std::nullopt) {
318*795d594fSAndroid Build Coastguard Worker     DumpPageInfo(virtual_page_index.value(), proc_files, os, page_size);
319*795d594fSAndroid Build Coastguard Worker   }
320*795d594fSAndroid Build Coastguard Worker   if (count_zero_pages) {
321*795d594fSAndroid Build Coastguard Worker     CountZeroPages(pid, proc_files, os, page_size);
322*795d594fSAndroid Build Coastguard Worker   }
323*795d594fSAndroid Build Coastguard Worker   return EXIT_SUCCESS;
324*795d594fSAndroid Build Coastguard Worker }
325*795d594fSAndroid Build Coastguard Worker 
326*795d594fSAndroid Build Coastguard Worker struct PageInfoArgs : public CmdlineArgs {
327*795d594fSAndroid Build Coastguard Worker  protected:
328*795d594fSAndroid Build Coastguard Worker   using Base = CmdlineArgs;
329*795d594fSAndroid Build Coastguard Worker 
ParseCustomart::PageInfoArgs330*795d594fSAndroid Build Coastguard Worker   ParseStatus ParseCustom(const char* raw_option,
331*795d594fSAndroid Build Coastguard Worker                           size_t raw_option_length,
332*795d594fSAndroid Build Coastguard Worker                           std::string* error_msg) override {
333*795d594fSAndroid Build Coastguard Worker     DCHECK_EQ(strlen(raw_option), raw_option_length);
334*795d594fSAndroid Build Coastguard Worker     {
335*795d594fSAndroid Build Coastguard Worker       ParseStatus base_parse = Base::ParseCustom(raw_option, raw_option_length, error_msg);
336*795d594fSAndroid Build Coastguard Worker       if (base_parse != kParseUnknownArgument) {
337*795d594fSAndroid Build Coastguard Worker         return base_parse;
338*795d594fSAndroid Build Coastguard Worker       }
339*795d594fSAndroid Build Coastguard Worker     }
340*795d594fSAndroid Build Coastguard Worker 
341*795d594fSAndroid Build Coastguard Worker     std::string_view option(raw_option, raw_option_length);
342*795d594fSAndroid Build Coastguard Worker     if (option.starts_with("--pid=")) {
343*795d594fSAndroid Build Coastguard Worker       // static_assert(std::is_signed_t
344*795d594fSAndroid Build Coastguard Worker       const char* value = raw_option + strlen("--pid=");
345*795d594fSAndroid Build Coastguard Worker       if (!android::base::ParseInt(value, &pid_)) {
346*795d594fSAndroid Build Coastguard Worker         *error_msg = "Failed to parse pid";
347*795d594fSAndroid Build Coastguard Worker         return kParseError;
348*795d594fSAndroid Build Coastguard Worker       }
349*795d594fSAndroid Build Coastguard Worker     } else if (option == "--count-zero-pages") {
350*795d594fSAndroid Build Coastguard Worker       count_zero_pages_ = true;
351*795d594fSAndroid Build Coastguard Worker     } else if (option.starts_with("--dump-page-info=")) {
352*795d594fSAndroid Build Coastguard Worker       const char* value = raw_option + strlen("--dump-page-info=");
353*795d594fSAndroid Build Coastguard Worker       virtual_page_index_ = 0;
354*795d594fSAndroid Build Coastguard Worker       if (!android::base::ParseUint(value, &virtual_page_index_.value())) {
355*795d594fSAndroid Build Coastguard Worker         *error_msg = "Failed to parse virtual page index";
356*795d594fSAndroid Build Coastguard Worker         return kParseError;
357*795d594fSAndroid Build Coastguard Worker       }
358*795d594fSAndroid Build Coastguard Worker     } else {
359*795d594fSAndroid Build Coastguard Worker       return kParseUnknownArgument;
360*795d594fSAndroid Build Coastguard Worker     }
361*795d594fSAndroid Build Coastguard Worker 
362*795d594fSAndroid Build Coastguard Worker     return kParseOk;
363*795d594fSAndroid Build Coastguard Worker   }
364*795d594fSAndroid Build Coastguard Worker 
ParseChecksart::PageInfoArgs365*795d594fSAndroid Build Coastguard Worker   ParseStatus ParseChecks(std::string* error_msg) override {
366*795d594fSAndroid Build Coastguard Worker     // Perform the parent checks.
367*795d594fSAndroid Build Coastguard Worker     ParseStatus parent_checks = Base::ParseChecks(error_msg);
368*795d594fSAndroid Build Coastguard Worker     if (parent_checks != kParseOk) {
369*795d594fSAndroid Build Coastguard Worker       return parent_checks;
370*795d594fSAndroid Build Coastguard Worker     }
371*795d594fSAndroid Build Coastguard Worker     if (pid_ == -1) {
372*795d594fSAndroid Build Coastguard Worker       *error_msg = "Missing --pid=";
373*795d594fSAndroid Build Coastguard Worker       return kParseError;
374*795d594fSAndroid Build Coastguard Worker     }
375*795d594fSAndroid Build Coastguard Worker 
376*795d594fSAndroid Build Coastguard Worker     // Perform our own checks.
377*795d594fSAndroid Build Coastguard Worker     if (kill(pid_, /*sig*/ 0) != 0) {  // No signal is sent, perform error-checking only.
378*795d594fSAndroid Build Coastguard Worker       // Check if the pid exists before proceeding.
379*795d594fSAndroid Build Coastguard Worker       if (errno == ESRCH) {
380*795d594fSAndroid Build Coastguard Worker         *error_msg = "Process specified does not exist, pid: " + std::to_string(pid_);
381*795d594fSAndroid Build Coastguard Worker       } else {
382*795d594fSAndroid Build Coastguard Worker         *error_msg = StringPrintf("Failed to check process status: %s", strerror(errno));
383*795d594fSAndroid Build Coastguard Worker       }
384*795d594fSAndroid Build Coastguard Worker       return kParseError;
385*795d594fSAndroid Build Coastguard Worker     }
386*795d594fSAndroid Build Coastguard Worker     return kParseOk;
387*795d594fSAndroid Build Coastguard Worker   }
388*795d594fSAndroid Build Coastguard Worker 
GetUsageart::PageInfoArgs389*795d594fSAndroid Build Coastguard Worker   std::string GetUsage() const override {
390*795d594fSAndroid Build Coastguard Worker     std::string usage;
391*795d594fSAndroid Build Coastguard Worker 
392*795d594fSAndroid Build Coastguard Worker     usage +=
393*795d594fSAndroid Build Coastguard Worker         "Usage: pageinfo [options] ...\n"
394*795d594fSAndroid Build Coastguard Worker         "    Example: pageinfo --pid=$(pidof system_server) --count-zero-pages\n"
395*795d594fSAndroid Build Coastguard Worker         "    Example: adb shell pageinfo --pid=$(pid system_server) --dump-page-info=0x70000000\n"
396*795d594fSAndroid Build Coastguard Worker         "\n";
397*795d594fSAndroid Build Coastguard Worker 
398*795d594fSAndroid Build Coastguard Worker     usage += Base::GetUsage();
399*795d594fSAndroid Build Coastguard Worker 
400*795d594fSAndroid Build Coastguard Worker     usage +=
401*795d594fSAndroid Build Coastguard Worker         "  --pid=<pid>: PID of the process to analyze.\n"
402*795d594fSAndroid Build Coastguard Worker         "  --count-zero-pages: output zero filled page stats for memory mappings of "
403*795d594fSAndroid Build Coastguard Worker         "<image-diff-pid> process.\n"
404*795d594fSAndroid Build Coastguard Worker         "  --dump-page-info=<virtual_page_index>: output PFN, kpagecount and kpageflags of a "
405*795d594fSAndroid Build Coastguard Worker         "virtual page in <image-diff-pid> process memory space.\n";
406*795d594fSAndroid Build Coastguard Worker 
407*795d594fSAndroid Build Coastguard Worker     return usage;
408*795d594fSAndroid Build Coastguard Worker   }
409*795d594fSAndroid Build Coastguard Worker 
410*795d594fSAndroid Build Coastguard Worker  public:
411*795d594fSAndroid Build Coastguard Worker   pid_t pid_ = -1;
412*795d594fSAndroid Build Coastguard Worker   bool count_zero_pages_ = false;
413*795d594fSAndroid Build Coastguard Worker   std::optional<uint64_t> virtual_page_index_;
414*795d594fSAndroid Build Coastguard Worker };
415*795d594fSAndroid Build Coastguard Worker 
416*795d594fSAndroid Build Coastguard Worker struct PageInfoMain : public CmdlineMain<PageInfoArgs> {
ExecuteWithoutRuntimeart::PageInfoMain417*795d594fSAndroid Build Coastguard Worker   bool ExecuteWithoutRuntime() override {
418*795d594fSAndroid Build Coastguard Worker     CHECK(args_ != nullptr);
419*795d594fSAndroid Build Coastguard Worker     CHECK(args_->os_ != nullptr);
420*795d594fSAndroid Build Coastguard Worker 
421*795d594fSAndroid Build Coastguard Worker     return PageInfo(
422*795d594fSAndroid Build Coastguard Worker                *args_->os_, args_->pid_, args_->count_zero_pages_, args_->virtual_page_index_,
423*795d594fSAndroid Build Coastguard Worker                MemMap::GetPageSize()) ==
424*795d594fSAndroid Build Coastguard Worker            EXIT_SUCCESS;
425*795d594fSAndroid Build Coastguard Worker   }
426*795d594fSAndroid Build Coastguard Worker 
NeedsRuntimeart::PageInfoMain427*795d594fSAndroid Build Coastguard Worker   bool NeedsRuntime() override { return false; }
428*795d594fSAndroid Build Coastguard Worker };
429*795d594fSAndroid Build Coastguard Worker 
430*795d594fSAndroid Build Coastguard Worker }  // namespace art
431*795d594fSAndroid Build Coastguard Worker 
main(int argc,char ** argv)432*795d594fSAndroid Build Coastguard Worker int main(int argc, char** argv) {
433*795d594fSAndroid Build Coastguard Worker   art::PageInfoMain main;
434*795d594fSAndroid Build Coastguard Worker   return main.Main(argc, argv);
435*795d594fSAndroid Build Coastguard Worker }
436