Chromium Code Reviews| 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 <sys/types.h> | 7 #include <sys/types.h> |
| 8 #include <unistd.h> | 8 #include <unistd.h> |
| 9 | 9 |
| 10 #include <algorithm> | 10 #include <algorithm> |
| 11 #include <cstring> | 11 #include <cstring> |
| 12 #include <fstream> | 12 #include <fstream> |
| 13 #include <iostream> | 13 #include <iostream> |
| 14 #include <limits> | 14 #include <limits> |
| 15 #include <string> | 15 #include <string> |
| 16 #include <utility> | 16 #include <utility> |
| 17 #include <vector> | 17 #include <vector> |
| 18 | 18 |
| 19 #include "base/base64.h" | |
| 19 #include "base/basictypes.h" | 20 #include "base/basictypes.h" |
| 20 #include "base/bind.h" | 21 #include "base/bind.h" |
| 21 #include "base/bind_helpers.h" | 22 #include "base/bind_helpers.h" |
| 22 #include "base/containers/hash_tables.h" | 23 #include "base/containers/hash_tables.h" |
| 23 #include "base/file_util.h" | 24 #include "base/file_util.h" |
| 24 #include "base/logging.h" | 25 #include "base/logging.h" |
| 25 #include "base/memory/scoped_ptr.h" | 26 #include "base/memory/scoped_ptr.h" |
| 26 #include "base/strings/string_number_conversions.h" | 27 #include "base/strings/string_number_conversions.h" |
| 27 #include "base/strings/string_piece.h" | 28 #include "base/strings/string_piece.h" |
| 28 #include "base/strings/string_split.h" | 29 #include "base/strings/string_split.h" |
| 29 #include "base/strings/stringprintf.h" | 30 #include "base/strings/stringprintf.h" |
| 30 | 31 |
| 31 namespace { | 32 namespace { |
| 32 | 33 |
| 34 class BitSet { | |
| 35 public: | |
| 36 BitSet() : data_(0) {} | |
| 37 | |
| 38 void resize(int nbits) { | |
|
bulach
2013/07/23 08:10:58
nit: size_t
Primiano Tucci (use gerrit)
2013/07/23 08:54:00
Done.
| |
| 39 data_.resize((nbits + 7) / 8); | |
| 40 } | |
| 41 | |
| 42 void set(int bit) { | |
|
bulach
2013/07/23 08:10:58
nit: uint32
Primiano Tucci (use gerrit)
2013/07/23 08:54:00
Done.
| |
| 43 const int byte_idx = bit / 8; | |
| 44 CHECK(byte_idx < data_.size()); | |
| 45 data_[byte_idx] |= (1 << (bit & 7)); | |
| 46 } | |
| 47 | |
| 48 const std::string AsB64String() const { | |
| 49 std::string bits(&data_[0], data_.size()); | |
| 50 std::string b64_string; | |
| 51 base::Base64Encode(bits, &b64_string); | |
| 52 return b64_string; | |
| 53 } | |
| 54 | |
| 55 private: | |
| 56 std::vector<char> data_; | |
|
bulach
2013/07/23 08:10:58
nit: uint8
Primiano Tucci (use gerrit)
2013/07/23 08:54:00
Hmm if I do that, than I have to add more boilerpl
| |
| 57 }; | |
| 58 | |
| 33 // An entry in /proc/<pid>/pagemap. | 59 // An entry in /proc/<pid>/pagemap. |
| 34 struct PageMapEntry { | 60 struct PageMapEntry { |
| 35 uint64 page_frame_number : 55; | 61 uint64 page_frame_number : 55; |
| 36 uint unused : 8; | 62 uint unused : 8; |
| 37 uint present : 1; | 63 uint present : 1; |
| 38 }; | 64 }; |
| 39 | 65 |
| 40 // Describes a memory page. | 66 // Describes a memory page. |
| 41 struct PageInfo { | 67 struct PageInfo { |
| 42 int64 page_frame_number; // Physical page id, also known as PFN. | 68 int64 page_frame_number; // Physical page id, also known as PFN. |
| 43 int64 flags; | 69 int64 flags; |
| 44 int32 times_mapped; | 70 int32 times_mapped; |
| 45 }; | 71 }; |
| 46 | 72 |
| 47 struct MemoryMap { | 73 struct MemoryMap { |
| 48 std::string name; | 74 std::string name; |
| 49 std::string flags; | 75 std::string flags; |
| 50 uint start_address; | 76 uint start_address; |
| 51 uint end_address; | 77 uint end_address; |
| 78 uint offset; | |
| 52 int private_count; | 79 int private_count; |
| 53 int unevictable_private_count; | 80 int unevictable_private_count; |
| 54 int other_shared_count; | 81 int other_shared_count; |
| 55 int unevictable_other_shared_count; | 82 int unevictable_other_shared_count; |
| 56 // app_shared_counts[i] contains the number of pages mapped in i+2 processes | 83 // app_shared_counts[i] contains the number of pages mapped in i+2 processes |
| 57 // (only among the processes that are being analyzed). | 84 // (only among the processes that are being analyzed). |
| 58 std::vector<int> app_shared_counts; | 85 std::vector<int> app_shared_counts; |
| 59 std::vector<PageInfo> committed_pages; | 86 std::vector<PageInfo> committed_pages; |
| 87 // committed_pages_bits is a bitset reflecting the present bit for all the | |
| 88 // virtual pages of the mapping. | |
| 89 BitSet committed_pages_bits; | |
| 60 }; | 90 }; |
| 61 | 91 |
| 62 struct ProcessMemory { | 92 struct ProcessMemory { |
| 63 pid_t pid; | 93 pid_t pid; |
| 64 std::vector<MemoryMap> memory_maps; | 94 std::vector<MemoryMap> memory_maps; |
| 65 }; | 95 }; |
| 66 | 96 |
| 67 bool PageIsUnevictable(const PageInfo& page_info) { | 97 bool PageIsUnevictable(const PageInfo& page_info) { |
| 68 // These constants are taken from kernel-page-flags.h. | 98 // These constants are taken from kernel-page-flags.h. |
| 69 const int KPF_DIRTY = 4; // Note that only file-mapped pages can be DIRTY. | 99 const int KPF_DIRTY = 4; // Note that only file-mapped pages can be DIRTY. |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 107 base::StringPiece( | 137 base::StringPiece( |
| 108 addr_range.begin() + end_addr_start_pos, | 138 addr_range.begin() + end_addr_start_pos, |
| 109 addr_range.begin() + end_addr_start_pos + addr_len), | 139 addr_range.begin() + end_addr_start_pos + addr_len), |
| 110 &tmp)) { | 140 &tmp)) { |
| 111 return false; | 141 return false; |
| 112 } | 142 } |
| 113 memory_map->end_address = static_cast<uint>(tmp); | 143 memory_map->end_address = static_cast<uint>(tmp); |
| 114 if (tokens->at(1).size() != strlen("rwxp")) | 144 if (tokens->at(1).size() != strlen("rwxp")) |
| 115 return false; | 145 return false; |
| 116 memory_map->flags.swap(tokens->at(1)); | 146 memory_map->flags.swap(tokens->at(1)); |
| 147 if (!base::HexStringToUInt64(tokens->at(2), &tmp)) | |
| 148 return false; | |
| 149 memory_map->offset = static_cast<uint>(tmp); | |
| 150 memory_map->committed_pages_bits.resize( | |
| 151 (memory_map->end_address - memory_map->start_address) / PAGE_SIZE); | |
| 117 const int map_name_index = 5; | 152 const int map_name_index = 5; |
| 118 if (tokens->size() >= map_name_index + 1) { | 153 if (tokens->size() >= map_name_index + 1) { |
| 119 for (std::vector<std::string>::const_iterator it = | 154 for (std::vector<std::string>::const_iterator it = |
| 120 tokens->begin() + map_name_index; it != tokens->end(); ++it) { | 155 tokens->begin() + map_name_index; it != tokens->end(); ++it) { |
| 121 if (!it->empty()) { | 156 if (!it->empty()) { |
| 122 if (!memory_map->name.empty()) | 157 if (!memory_map->name.empty()) |
| 123 memory_map->name.append(" "); | 158 memory_map->name.append(" "); |
| 124 memory_map->name.append(*it); | 159 memory_map->name.append(*it); |
| 125 } | 160 } |
| 126 } | 161 } |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 160 } | 195 } |
| 161 process_maps->push_back(memory_map); | 196 process_maps->push_back(memory_map); |
| 162 } | 197 } |
| 163 return true; | 198 return true; |
| 164 } | 199 } |
| 165 | 200 |
| 166 // Fills |committed_pages| in with the set of committed pages contained in the | 201 // Fills |committed_pages| in with the set of committed pages contained in the |
| 167 // provided memory map. | 202 // provided memory map. |
| 168 bool GetPagesForMemoryMap(int pagemap_fd, | 203 bool GetPagesForMemoryMap(int pagemap_fd, |
| 169 const MemoryMap& memory_map, | 204 const MemoryMap& memory_map, |
| 170 std::vector<PageInfo>* committed_pages) { | 205 std::vector<PageInfo>* committed_pages, |
| 171 for (uint addr = memory_map.start_address; addr < memory_map.end_address; | 206 BitSet* committed_pages_bits) { |
| 172 addr += PAGE_SIZE) { | 207 for (uint addr = memory_map.start_address, page_index = 0; |
| 208 addr < memory_map.end_address; | |
| 209 addr += PAGE_SIZE, ++page_index) { | |
| 173 DCHECK_EQ(0, addr % PAGE_SIZE); | 210 DCHECK_EQ(0, addr % PAGE_SIZE); |
| 174 PageMapEntry page_map_entry = {}; | 211 PageMapEntry page_map_entry = {}; |
| 175 COMPILE_ASSERT(sizeof(PageMapEntry) == sizeof(uint64), unexpected_size); | 212 COMPILE_ASSERT(sizeof(PageMapEntry) == sizeof(uint64), unexpected_size); |
| 176 const off64_t offset = addr / PAGE_SIZE; | 213 const off64_t offset = addr / PAGE_SIZE; |
| 177 if (!ReadFromFileAtOffset(pagemap_fd, offset, &page_map_entry)) | 214 if (!ReadFromFileAtOffset(pagemap_fd, offset, &page_map_entry)) |
| 178 return false; | 215 return false; |
| 179 if (page_map_entry.present) { // Ignore non-committed pages. | 216 if (page_map_entry.present) { // Ignore non-committed pages. |
| 180 if (page_map_entry.page_frame_number == 0) | 217 if (page_map_entry.page_frame_number == 0) |
| 181 continue; | 218 continue; |
| 182 PageInfo page_info = {}; | 219 PageInfo page_info = {}; |
| 183 page_info.page_frame_number = page_map_entry.page_frame_number; | 220 page_info.page_frame_number = page_map_entry.page_frame_number; |
| 184 committed_pages->push_back(page_info); | 221 committed_pages->push_back(page_info); |
| 222 committed_pages_bits->set(page_index); | |
| 185 } | 223 } |
| 186 } | 224 } |
| 187 return true; | 225 return true; |
| 188 } | 226 } |
| 189 | 227 |
| 190 // Fills |committed_pages| with mapping count and flags information gathered | 228 // Fills |committed_pages| with mapping count and flags information gathered |
| 191 // looking-up /proc/kpagecount and /proc/kpageflags. | 229 // looking-up /proc/kpagecount and /proc/kpageflags. |
| 192 bool SetPagesInfo(int pagecount_fd, | 230 bool SetPagesInfo(int pagecount_fd, |
| 193 int pageflags_fd, | 231 int pageflags_fd, |
| 194 std::vector<PageInfo>* pages) { | 232 std::vector<PageInfo>* pages) { |
| (...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 380 base::SStringPrintf( | 418 base::SStringPrintf( |
| 381 &buf, "%d\t%d\t\t%d\t\t%d\n", | 419 &buf, "%d\t%d\t\t%d\t\t%d\n", |
| 382 process_memory.pid, | 420 process_memory.pid, |
| 383 total_private * KB_PER_PAGE, | 421 total_private * KB_PER_PAGE, |
| 384 static_cast<int>(total_app_shared) * KB_PER_PAGE, | 422 static_cast<int>(total_app_shared) * KB_PER_PAGE, |
| 385 total_other_shared * KB_PER_PAGE); | 423 total_other_shared * KB_PER_PAGE); |
| 386 std::cout << buf; | 424 std::cout << buf; |
| 387 } | 425 } |
| 388 } | 426 } |
| 389 | 427 |
| 428 void DumpProcessesMemoryMapsInExtendedFormat( | |
| 429 const std::vector<ProcessMemory>& processes_memory) { | |
| 430 std::string buf; | |
| 431 std::string app_shared_buf; | |
| 432 for (std::vector<ProcessMemory>::const_iterator it = processes_memory.begin(); | |
| 433 it != processes_memory.end(); ++it) { | |
| 434 const ProcessMemory& process_memory = *it; | |
| 435 std::cout << "[ PID=" << process_memory.pid << "]" << '\n'; | |
| 436 const std::vector<MemoryMap>& memory_maps = process_memory.memory_maps; | |
| 437 for (std::vector<MemoryMap>::const_iterator it = memory_maps.begin(); | |
| 438 it != memory_maps.end(); ++it) { | |
| 439 const MemoryMap& memory_map = *it; | |
| 440 app_shared_buf.clear(); | |
| 441 AppendAppSharedField(memory_map.app_shared_counts, &app_shared_buf); | |
| 442 base::SStringPrintf( | |
| 443 &buf, | |
| 444 "%x-%x %s %x private_unevictable=%d private=%d shared_app=%s " | |
| 445 "shared_other_unevictable=%d shared_other=%d \"%s\" [%s]\n", | |
| 446 memory_map.start_address, | |
| 447 memory_map.end_address, | |
| 448 memory_map.flags.c_str(), | |
| 449 memory_map.offset, | |
| 450 memory_map.unevictable_private_count * PAGE_SIZE, | |
| 451 memory_map.private_count * PAGE_SIZE, | |
| 452 app_shared_buf.c_str(), | |
| 453 memory_map.unevictable_other_shared_count * PAGE_SIZE, | |
| 454 memory_map.other_shared_count * PAGE_SIZE, | |
| 455 memory_map.name.c_str(), | |
| 456 memory_map.committed_pages_bits.AsB64String().c_str()); | |
| 457 std::cout << buf; | |
| 458 } | |
| 459 } | |
| 460 } | |
| 461 | |
| 390 bool CollectProcessMemoryInformation(int page_count_fd, | 462 bool CollectProcessMemoryInformation(int page_count_fd, |
| 391 int page_flags_fd, | 463 int page_flags_fd, |
| 392 ProcessMemory* process_memory) { | 464 ProcessMemory* process_memory) { |
| 393 const pid_t pid = process_memory->pid; | 465 const pid_t pid = process_memory->pid; |
| 394 int pagemap_fd = open( | 466 int pagemap_fd = open( |
| 395 base::StringPrintf("/proc/%d/pagemap", pid).c_str(), O_RDONLY); | 467 base::StringPrintf("/proc/%d/pagemap", pid).c_str(), O_RDONLY); |
| 396 if (pagemap_fd < 0) { | 468 if (pagemap_fd < 0) { |
| 397 PLOG(ERROR) << "open"; | 469 PLOG(ERROR) << "open"; |
| 398 return false; | 470 return false; |
| 399 } | 471 } |
| 400 file_util::ScopedFD auto_closer(&pagemap_fd); | 472 file_util::ScopedFD auto_closer(&pagemap_fd); |
| 401 std::vector<MemoryMap>* const process_maps = &process_memory->memory_maps; | 473 std::vector<MemoryMap>* const process_maps = &process_memory->memory_maps; |
| 402 if (!GetProcessMaps(pid, process_maps)) | 474 if (!GetProcessMaps(pid, process_maps)) |
| 403 return false; | 475 return false; |
| 404 for (std::vector<MemoryMap>::iterator it = process_maps->begin(); | 476 for (std::vector<MemoryMap>::iterator it = process_maps->begin(); |
| 405 it != process_maps->end(); ++it) { | 477 it != process_maps->end(); ++it) { |
| 406 std::vector<PageInfo>* const committed_pages = &it->committed_pages; | 478 std::vector<PageInfo>* const committed_pages = &it->committed_pages; |
| 407 GetPagesForMemoryMap(pagemap_fd, *it, committed_pages); | 479 BitSet* const pages_bits = &it->committed_pages_bits; |
| 480 GetPagesForMemoryMap(pagemap_fd, *it, committed_pages, pages_bits); | |
| 408 SetPagesInfo(page_count_fd, page_flags_fd, committed_pages); | 481 SetPagesInfo(page_count_fd, page_flags_fd, committed_pages); |
| 409 } | 482 } |
| 410 return true; | 483 return true; |
| 411 } | 484 } |
| 412 | 485 |
| 413 void KillAll(const std::vector<pid_t>& pids, int signal_number) { | 486 void KillAll(const std::vector<pid_t>& pids, int signal_number) { |
| 414 for (std::vector<pid_t>::const_iterator it = pids.begin(); it != pids.end(); | 487 for (std::vector<pid_t>::const_iterator it = pids.begin(); it != pids.end(); |
| 415 ++it) { | 488 ++it) { |
| 416 kill(*it, signal_number); | 489 kill(*it, signal_number); |
| 417 } | 490 } |
| 418 } | 491 } |
| 419 | 492 |
| 420 } // namespace | 493 } // namespace |
| 421 | 494 |
| 422 int main(int argc, char** argv) { | 495 int main(int argc, char** argv) { |
| 423 bool short_output = false; | |
| 424 if (argc == 1) { | 496 if (argc == 1) { |
| 425 LOG(ERROR) << "Usage: " << argv[0] << " [-a] <PID1>... <PIDN>"; | 497 LOG(ERROR) << "Usage: " << argv[0] << " [-a|-x] <PID1>... <PIDN>"; |
| 426 return EXIT_FAILURE; | 498 return EXIT_FAILURE; |
| 427 } | 499 } |
| 428 if (!strncmp(argv[1], "-a", 2)) { | 500 const bool short_output = !strncmp(argv[1], "-a", 2); |
| 501 const bool extended_output = !strncmp(argv[1], "-x", 2); | |
| 502 if (short_output || extended_output) { | |
| 429 if (argc == 2) { | 503 if (argc == 2) { |
| 430 LOG(ERROR) << "Usage: " << argv[0] << " [-a] <PID1>... <PIDN>"; | 504 LOG(ERROR) << "Usage: " << argv[0] << " [-a|-x] <PID1>... <PIDN>"; |
| 431 return EXIT_FAILURE; | 505 return EXIT_FAILURE; |
| 432 } | 506 } |
| 433 short_output = true; | |
| 434 ++argv; | 507 ++argv; |
| 435 } | 508 } |
| 436 std::vector<pid_t> pids; | 509 std::vector<pid_t> pids; |
| 437 for (const char* const* ptr = argv + 1; *ptr; ++ptr) { | 510 for (const char* const* ptr = argv + 1; *ptr; ++ptr) { |
| 438 pid_t pid; | 511 pid_t pid; |
| 439 if (!base::StringToInt(*ptr, &pid)) | 512 if (!base::StringToInt(*ptr, &pid)) |
| 440 return EXIT_FAILURE; | 513 return EXIT_FAILURE; |
| 441 pids.push_back(pid); | 514 pids.push_back(pid); |
| 442 } | 515 } |
| 443 | 516 |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 469 if (!CollectProcessMemoryInformation(page_count_fd, | 542 if (!CollectProcessMemoryInformation(page_count_fd, |
| 470 page_flags_fd, | 543 page_flags_fd, |
| 471 process_memory)) | 544 process_memory)) |
| 472 return EXIT_FAILURE; | 545 return EXIT_FAILURE; |
| 473 } | 546 } |
| 474 } | 547 } |
| 475 | 548 |
| 476 ClassifyPages(&processes_memory); | 549 ClassifyPages(&processes_memory); |
| 477 if (short_output) | 550 if (short_output) |
| 478 DumpProcessesMemoryMapsInShortFormat(processes_memory); | 551 DumpProcessesMemoryMapsInShortFormat(processes_memory); |
| 552 else if (extended_output) | |
| 553 DumpProcessesMemoryMapsInExtendedFormat(processes_memory); | |
| 479 else | 554 else |
| 480 DumpProcessesMemoryMaps(processes_memory); | 555 DumpProcessesMemoryMaps(processes_memory); |
| 481 return EXIT_SUCCESS; | 556 return EXIT_SUCCESS; |
| 482 } | 557 } |
| OLD | NEW |