| 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 |