OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <fcntl.h> | 5 #include <fcntl.h> |
6 #include <signal.h> | 6 #include <signal.h> |
| 7 #include <stddef.h> |
| 8 #include <stdint.h> |
7 #include <sys/types.h> | 9 #include <sys/types.h> |
8 #include <unistd.h> | 10 #include <unistd.h> |
9 | 11 |
10 #include <algorithm> | 12 #include <algorithm> |
11 #include <cstring> | 13 #include <cstring> |
12 #include <fstream> | 14 #include <fstream> |
13 #include <iostream> | 15 #include <iostream> |
14 #include <limits> | 16 #include <limits> |
15 #include <string> | 17 #include <string> |
16 #include <utility> | 18 #include <utility> |
17 #include <vector> | 19 #include <vector> |
18 | 20 |
19 #include "base/base64.h" | 21 #include "base/base64.h" |
20 #include "base/basictypes.h" | |
21 #include "base/bind.h" | 22 #include "base/bind.h" |
22 #include "base/callback_helpers.h" | 23 #include "base/callback_helpers.h" |
23 #include "base/containers/hash_tables.h" | 24 #include "base/containers/hash_tables.h" |
24 #include "base/files/file_util.h" | 25 #include "base/files/file_util.h" |
25 #include "base/files/scoped_file.h" | 26 #include "base/files/scoped_file.h" |
26 #include "base/format_macros.h" | 27 #include "base/format_macros.h" |
27 #include "base/logging.h" | 28 #include "base/logging.h" |
28 #include "base/strings/string_number_conversions.h" | 29 #include "base/strings/string_number_conversions.h" |
29 #include "base/strings/string_split.h" | 30 #include "base/strings/string_split.h" |
30 #include "base/strings/stringprintf.h" | 31 #include "base/strings/stringprintf.h" |
31 | 32 |
32 const unsigned int kPageSize = getpagesize(); | 33 const unsigned int kPageSize = getpagesize(); |
33 | 34 |
34 namespace { | 35 namespace { |
35 | 36 |
36 class BitSet { | 37 class BitSet { |
37 public: | 38 public: |
38 void resize(size_t nbits) { | 39 void resize(size_t nbits) { |
39 data_.resize((nbits + 7) / 8); | 40 data_.resize((nbits + 7) / 8); |
40 } | 41 } |
41 | 42 |
42 void set(uint32 bit) { | 43 void set(uint32_t bit) { |
43 const uint32 byte_idx = bit / 8; | 44 const uint32_t byte_idx = bit / 8; |
44 CHECK(byte_idx < data_.size()); | 45 CHECK(byte_idx < data_.size()); |
45 data_[byte_idx] |= (1 << (bit & 7)); | 46 data_[byte_idx] |= (1 << (bit & 7)); |
46 } | 47 } |
47 | 48 |
48 std::string AsB64String() const { | 49 std::string AsB64String() const { |
49 // Simple optimization: strip trailing zero bytes from the bitmap. | 50 // Simple optimization: strip trailing zero bytes from the bitmap. |
50 // For instance, if a region has 32 pages but only the first 9 are resident, | 51 // For instance, if a region has 32 pages but only the first 9 are resident, |
51 // The full bitmap would be 0xff 0x01 0x00 0x00, the stripped one 0xff 0x01. | 52 // The full bitmap would be 0xff 0x01 0x00 0x00, the stripped one 0xff 0x01. |
52 // It can save up to some seconds when printing large mmaps, in particular | 53 // It can save up to some seconds when printing large mmaps, in particular |
53 // in presence of large virtual address space reservations (where none of | 54 // in presence of large virtual address space reservations (where none of |
54 // the pages are resident). | 55 // the pages are resident). |
55 size_t end = data_.size(); | 56 size_t end = data_.size(); |
56 while (end > 0 && data_[end - 1] == '\0') | 57 while (end > 0 && data_[end - 1] == '\0') |
57 --end; | 58 --end; |
58 std::string bits(&data_[0], end); | 59 std::string bits(&data_[0], end); |
59 std::string b64_string; | 60 std::string b64_string; |
60 base::Base64Encode(bits, &b64_string); | 61 base::Base64Encode(bits, &b64_string); |
61 return b64_string; | 62 return b64_string; |
62 } | 63 } |
63 | 64 |
64 private: | 65 private: |
65 std::vector<char> data_; | 66 std::vector<char> data_; |
66 }; | 67 }; |
67 | 68 |
68 // An entry in /proc/<pid>/pagemap. | 69 // An entry in /proc/<pid>/pagemap. |
69 struct PageMapEntry { | 70 struct PageMapEntry { |
70 uint64 page_frame_number : 55; | 71 uint64_t page_frame_number : 55; |
71 uint unused : 8; | 72 uint unused : 8; |
72 uint present : 1; | 73 uint present : 1; |
73 }; | 74 }; |
74 | 75 |
75 // Describes a memory page. | 76 // Describes a memory page. |
76 struct PageInfo { | 77 struct PageInfo { |
77 int64 page_frame_number; // Physical page id, also known as PFN. | 78 int64_t page_frame_number; // Physical page id, also known as PFN. |
78 int64 flags; | 79 int64_t flags; |
79 int32 times_mapped; | 80 int32_t times_mapped; |
80 }; | 81 }; |
81 | 82 |
82 struct PageCount { | 83 struct PageCount { |
83 PageCount() : total_count(0), unevictable_count(0) {} | 84 PageCount() : total_count(0), unevictable_count(0) {} |
84 | 85 |
85 int total_count; | 86 int total_count; |
86 int unevictable_count; | 87 int unevictable_count; |
87 }; | 88 }; |
88 | 89 |
89 struct MemoryMap { | 90 struct MemoryMap { |
90 std::string name; | 91 std::string name; |
91 std::string flags; | 92 std::string flags; |
92 uint64 start_address; | 93 uint64_t start_address; |
93 uint64 end_address; | 94 uint64_t end_address; |
94 uint64 offset; | 95 uint64_t offset; |
95 PageCount private_pages; | 96 PageCount private_pages; |
96 // app_shared_pages[i] contains the number of pages mapped in i+2 processes | 97 // app_shared_pages[i] contains the number of pages mapped in i+2 processes |
97 // (only among the processes that are being analyzed). | 98 // (only among the processes that are being analyzed). |
98 std::vector<PageCount> app_shared_pages; | 99 std::vector<PageCount> app_shared_pages; |
99 PageCount other_shared_pages; | 100 PageCount other_shared_pages; |
100 std::vector<PageInfo> committed_pages; | 101 std::vector<PageInfo> committed_pages; |
101 // committed_pages_bits is a bitset reflecting the present bit for all the | 102 // committed_pages_bits is a bitset reflecting the present bit for all the |
102 // virtual pages of the mapping. | 103 // virtual pages of the mapping. |
103 BitSet committed_pages_bits; | 104 BitSet committed_pages_bits; |
104 }; | 105 }; |
(...skipping 11 matching lines...) Expand all Loading... |
116 const int KPF_MLOCKED = 33; | 117 const int KPF_MLOCKED = 33; |
117 | 118 |
118 return (page_info.flags & ((1ll << KPF_DIRTY) | | 119 return (page_info.flags & ((1ll << KPF_DIRTY) | |
119 (1ll << KPF_ANON) | | 120 (1ll << KPF_ANON) | |
120 (1ll << KPF_UNEVICTABLE) | | 121 (1ll << KPF_UNEVICTABLE) | |
121 (1ll << KPF_MLOCKED))) ? | 122 (1ll << KPF_MLOCKED))) ? |
122 true : false; | 123 true : false; |
123 } | 124 } |
124 | 125 |
125 // Number of times a physical page is mapped in a process. | 126 // Number of times a physical page is mapped in a process. |
126 typedef base::hash_map<uint64, int> PFNMap; | 127 typedef base::hash_map<uint64_t, int> PFNMap; |
127 | 128 |
128 // Parses lines from /proc/<PID>/maps, e.g.: | 129 // Parses lines from /proc/<PID>/maps, e.g.: |
129 // 401e7000-401f5000 r-xp 00000000 103:02 158 /system/bin/linker | 130 // 401e7000-401f5000 r-xp 00000000 103:02 158 /system/bin/linker |
130 bool ParseMemoryMapLine(const std::string& line, | 131 bool ParseMemoryMapLine(const std::string& line, |
131 std::vector<std::string>* tokens, | 132 std::vector<std::string>* tokens, |
132 MemoryMap* memory_map) { | 133 MemoryMap* memory_map) { |
133 *tokens = base::SplitString( | 134 *tokens = base::SplitString( |
134 line, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); | 135 line, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
135 if (tokens->size() < 2) | 136 if (tokens->size() < 2) |
136 return false; | 137 return false; |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
206 // provided memory map. | 207 // provided memory map. |
207 bool GetPagesForMemoryMap(int pagemap_fd, | 208 bool GetPagesForMemoryMap(int pagemap_fd, |
208 const MemoryMap& memory_map, | 209 const MemoryMap& memory_map, |
209 std::vector<PageInfo>* committed_pages, | 210 std::vector<PageInfo>* committed_pages, |
210 BitSet* committed_pages_bits) { | 211 BitSet* committed_pages_bits) { |
211 const off64_t offset = memory_map.start_address / kPageSize; | 212 const off64_t offset = memory_map.start_address / kPageSize; |
212 if (lseek64(pagemap_fd, offset * sizeof(PageMapEntry), SEEK_SET) < 0) { | 213 if (lseek64(pagemap_fd, offset * sizeof(PageMapEntry), SEEK_SET) < 0) { |
213 PLOG(ERROR) << "lseek"; | 214 PLOG(ERROR) << "lseek"; |
214 return false; | 215 return false; |
215 } | 216 } |
216 for (uint64 addr = memory_map.start_address, page_index = 0; | 217 for (uint64_t addr = memory_map.start_address, page_index = 0; |
217 addr < memory_map.end_address; | 218 addr < memory_map.end_address; addr += kPageSize, ++page_index) { |
218 addr += kPageSize, ++page_index) { | |
219 DCHECK_EQ(0u, addr % kPageSize); | 219 DCHECK_EQ(0u, addr % kPageSize); |
220 PageMapEntry page_map_entry = {}; | 220 PageMapEntry page_map_entry = {}; |
221 static_assert(sizeof(PageMapEntry) == sizeof(uint64), "unexpected size"); | 221 static_assert(sizeof(PageMapEntry) == sizeof(uint64_t), "unexpected size"); |
222 ssize_t bytes = read(pagemap_fd, &page_map_entry, sizeof(page_map_entry)); | 222 ssize_t bytes = read(pagemap_fd, &page_map_entry, sizeof(page_map_entry)); |
223 if (bytes != sizeof(PageMapEntry) && bytes != 0) { | 223 if (bytes != sizeof(PageMapEntry) && bytes != 0) { |
224 PLOG(ERROR) << "read"; | 224 PLOG(ERROR) << "read"; |
225 return false; | 225 return false; |
226 } | 226 } |
227 if (page_map_entry.present) { // Ignore non-committed pages. | 227 if (page_map_entry.present) { // Ignore non-committed pages. |
228 if (page_map_entry.page_frame_number == 0) | 228 if (page_map_entry.page_frame_number == 0) |
229 continue; | 229 continue; |
230 PageInfo page_info = {}; | 230 PageInfo page_info = {}; |
231 page_info.page_frame_number = page_map_entry.page_frame_number; | 231 page_info.page_frame_number = page_map_entry.page_frame_number; |
232 committed_pages->push_back(page_info); | 232 committed_pages->push_back(page_info); |
233 committed_pages_bits->set(page_index); | 233 committed_pages_bits->set(page_index); |
234 } | 234 } |
235 } | 235 } |
236 return true; | 236 return true; |
237 } | 237 } |
238 | 238 |
239 // Fills |committed_pages| with mapping count and flags information gathered | 239 // Fills |committed_pages| with mapping count and flags information gathered |
240 // looking-up /proc/kpagecount and /proc/kpageflags. | 240 // looking-up /proc/kpagecount and /proc/kpageflags. |
241 bool SetPagesInfo(int pagecount_fd, | 241 bool SetPagesInfo(int pagecount_fd, |
242 int pageflags_fd, | 242 int pageflags_fd, |
243 std::vector<PageInfo>* pages) { | 243 std::vector<PageInfo>* pages) { |
244 for (std::vector<PageInfo>::iterator it = pages->begin(); | 244 for (std::vector<PageInfo>::iterator it = pages->begin(); |
245 it != pages->end(); ++it) { | 245 it != pages->end(); ++it) { |
246 PageInfo* const page_info = &*it; | 246 PageInfo* const page_info = &*it; |
247 int64 times_mapped; | 247 int64_t times_mapped; |
248 if (!ReadFromFileAtOffset( | 248 if (!ReadFromFileAtOffset( |
249 pagecount_fd, page_info->page_frame_number, ×_mapped)) { | 249 pagecount_fd, page_info->page_frame_number, ×_mapped)) { |
250 return false; | 250 return false; |
251 } | 251 } |
252 DCHECK(times_mapped <= std::numeric_limits<int32_t>::max()); | 252 DCHECK(times_mapped <= std::numeric_limits<int32_t>::max()); |
253 page_info->times_mapped = static_cast<int32>(times_mapped); | 253 page_info->times_mapped = static_cast<int32_t>(times_mapped); |
254 | 254 |
255 int64 page_flags; | 255 int64_t page_flags; |
256 if (!ReadFromFileAtOffset( | 256 if (!ReadFromFileAtOffset( |
257 pageflags_fd, page_info->page_frame_number, &page_flags)) { | 257 pageflags_fd, page_info->page_frame_number, &page_flags)) { |
258 return false; | 258 return false; |
259 } | 259 } |
260 page_info->flags = page_flags; | 260 page_info->flags = page_flags; |
261 } | 261 } |
262 return true; | 262 return true; |
263 } | 263 } |
264 | 264 |
265 // Fills in the provided vector of Page Frame Number maps. This lets | 265 // Fills in the provided vector of Page Frame Number maps. This lets |
(...skipping 19 matching lines...) Expand all Loading... |
285 } | 285 } |
286 } | 286 } |
287 | 287 |
288 // Sets the private_count/app_shared_pages/other_shared_count fields of the | 288 // Sets the private_count/app_shared_pages/other_shared_count fields of the |
289 // provided memory maps for each process. | 289 // provided memory maps for each process. |
290 void ClassifyPages(std::vector<ProcessMemory>* processes_memory) { | 290 void ClassifyPages(std::vector<ProcessMemory>* processes_memory) { |
291 std::vector<PFNMap> pfn_maps(processes_memory->size()); | 291 std::vector<PFNMap> pfn_maps(processes_memory->size()); |
292 FillPFNMaps(*processes_memory, &pfn_maps); | 292 FillPFNMaps(*processes_memory, &pfn_maps); |
293 // Hash set keeping track of the physical pages mapped in a single process so | 293 // Hash set keeping track of the physical pages mapped in a single process so |
294 // that they can be counted only once. | 294 // that they can be counted only once. |
295 base::hash_set<uint64> physical_pages_mapped_in_process; | 295 base::hash_set<uint64_t> physical_pages_mapped_in_process; |
296 | 296 |
297 for (std::vector<ProcessMemory>::iterator it = processes_memory->begin(); | 297 for (std::vector<ProcessMemory>::iterator it = processes_memory->begin(); |
298 it != processes_memory->end(); ++it) { | 298 it != processes_memory->end(); ++it) { |
299 std::vector<MemoryMap>* const memory_maps = &it->memory_maps; | 299 std::vector<MemoryMap>* const memory_maps = &it->memory_maps; |
300 physical_pages_mapped_in_process.clear(); | 300 physical_pages_mapped_in_process.clear(); |
301 for (std::vector<MemoryMap>::iterator it = memory_maps->begin(); | 301 for (std::vector<MemoryMap>::iterator it = memory_maps->begin(); |
302 it != memory_maps->end(); ++it) { | 302 it != memory_maps->end(); ++it) { |
303 MemoryMap* const memory_map = &*it; | 303 MemoryMap* const memory_map = &*it; |
304 const size_t processes_count = processes_memory->size(); | 304 const size_t processes_count = processes_memory->size(); |
305 memory_map->app_shared_pages.resize(processes_count - 1); | 305 memory_map->app_shared_pages.resize(processes_count - 1); |
306 const std::vector<PageInfo>& pages = memory_map->committed_pages; | 306 const std::vector<PageInfo>& pages = memory_map->committed_pages; |
307 for (std::vector<PageInfo>::const_iterator it = pages.begin(); | 307 for (std::vector<PageInfo>::const_iterator it = pages.begin(); |
308 it != pages.end(); ++it) { | 308 it != pages.end(); ++it) { |
309 const PageInfo& page_info = *it; | 309 const PageInfo& page_info = *it; |
310 if (page_info.times_mapped == 1) { | 310 if (page_info.times_mapped == 1) { |
311 ++memory_map->private_pages.total_count; | 311 ++memory_map->private_pages.total_count; |
312 if (PageIsUnevictable(page_info)) | 312 if (PageIsUnevictable(page_info)) |
313 ++memory_map->private_pages.unevictable_count; | 313 ++memory_map->private_pages.unevictable_count; |
314 continue; | 314 continue; |
315 } | 315 } |
316 const uint64 page_frame_number = page_info.page_frame_number; | 316 const uint64_t page_frame_number = page_info.page_frame_number; |
317 const std::pair<base::hash_set<uint64>::iterator, bool> result = | 317 const std::pair<base::hash_set<uint64_t>::iterator, bool> result = |
318 physical_pages_mapped_in_process.insert(page_frame_number); | 318 physical_pages_mapped_in_process.insert(page_frame_number); |
319 const bool did_insert = result.second; | 319 const bool did_insert = result.second; |
320 if (!did_insert) { | 320 if (!did_insert) { |
321 // This physical page (mapped multiple times in the same process) was | 321 // This physical page (mapped multiple times in the same process) was |
322 // already counted. | 322 // already counted. |
323 continue; | 323 continue; |
324 } | 324 } |
325 // See if the current physical page is also mapped in the other | 325 // See if the current physical page is also mapped in the other |
326 // processes that are being analyzed. | 326 // processes that are being analyzed. |
327 int times_mapped = 0; | 327 int times_mapped = 0; |
(...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
525 } | 525 } |
526 } | 526 } |
527 | 527 |
528 ClassifyPages(&processes_memory); | 528 ClassifyPages(&processes_memory); |
529 if (short_output) | 529 if (short_output) |
530 DumpProcessesMemoryMapsInShortFormat(processes_memory); | 530 DumpProcessesMemoryMapsInShortFormat(processes_memory); |
531 else | 531 else |
532 DumpProcessesMemoryMapsInExtendedFormat(processes_memory); | 532 DumpProcessesMemoryMapsInExtendedFormat(processes_memory); |
533 return EXIT_SUCCESS; | 533 return EXIT_SUCCESS; |
534 } | 534 } |
OLD | NEW |