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 { | |
bulach
2013/07/22 12:59:39
I suppose the ndk has #include <bitset> ?
digit1
2013/07/22 21:03:14
That's true, but note that std::bitset<> is not a
Primiano Tucci (use gerrit)
2013/07/22 23:22:35
Yeah, unforunately STL's bitset require the size o
bulach
2013/07/23 08:10:57
ops, forgot that tiny detail :) thanks for the ref
| |
35 public: | |
Philippe
2013/07/22 09:20:55
Nit: there should be a leading space before 'publi
Primiano Tucci (use gerrit)
2013/07/22 23:22:35
Done.
| |
36 BitSet() : nbits_(0), data_(0) { } | |
Philippe
2013/07/22 09:20:55
Nit: no space between the braces.
Philippe
2013/07/22 09:20:55
Nit: I believe the |data_| explicit initialization
Primiano Tucci (use gerrit)
2013/07/22 23:22:35
Done.
Primiano Tucci (use gerrit)
2013/07/22 23:22:35
Hmm, agree for nbits (see below), but _data was ju
| |
37 | |
38 void setSize(int nbits) { | |
Philippe
2013/07/22 09:20:55
Nit: maybe call this 'resize()' and also s/getData
digit1
2013/07/22 21:03:14
Also, you should use the Chromium style guide, whi
Primiano Tucci (use gerrit)
2013/07/22 23:22:35
Right, makes sense!
| |
39 nbits_ = nbits; | |
Philippe
2013/07/22 09:20:55
Nit: do we need to store |nbits|? Can we entirely
Primiano Tucci (use gerrit)
2013/07/22 23:22:35
Yeah, that was just me being uber-paranoid. The at
| |
40 data_.resize((nbits_+7)/8); | |
Philippe
2013/07/22 09:20:55
Nit: spaces around binary operators (here and on a
Primiano Tucci (use gerrit)
2013/07/22 23:22:35
Done.
| |
41 memset(&data_[0], 0, getLength()); | |
Philippe
2013/07/22 09:20:55
Nit: I believe you can delete this line and use th
Primiano Tucci (use gerrit)
2013/07/22 23:22:35
Oh, apparently I am the one missing the autofill o
| |
42 } | |
43 | |
44 void set(int bit) { | |
45 data_.at(bit/8) |= (1 << (bit % 8)); | |
digit1
2013/07/22 21:03:14
nit: since 'bit' is signed, using 'bit & 7' is mor
Primiano Tucci (use gerrit)
2013/07/22 23:22:35
Great catch! Right.
For the records:
return arg
| |
46 } | |
47 | |
48 const char* getData() const { return &data_[0]; } | |
Philippe
2013/07/22 09:20:55
Nit: data_.data()?
Primiano Tucci (use gerrit)
2013/07/22 23:22:35
I'm afraid that is only possible in C++11.
| |
49 | |
50 int getLength() const { return data_.size(); } | |
digit1
2013/07/22 21:03:14
nit: This is very misleading because this doesn't
Primiano Tucci (use gerrit)
2013/07/22 23:22:35
At this point, let's do directly AsB64String().
| |
51 | |
52 private: | |
53 int nbits_; | |
54 std::vector<char> data_; | |
55 }; | |
56 | |
33 // An entry in /proc/<pid>/pagemap. | 57 // An entry in /proc/<pid>/pagemap. |
34 struct PageMapEntry { | 58 struct PageMapEntry { |
35 uint64 page_frame_number : 55; | 59 uint64 page_frame_number : 55; |
36 uint unused : 8; | 60 uint unused : 8; |
37 uint present : 1; | 61 uint present : 1; |
38 }; | 62 }; |
39 | 63 |
40 // Describes a memory page. | 64 // Describes a memory page. |
41 struct PageInfo { | 65 struct PageInfo { |
42 int64 page_frame_number; // Physical page id, also known as PFN. | 66 int64 page_frame_number; // Physical page id, also known as PFN. |
43 int64 flags; | 67 int64 flags; |
44 int32 times_mapped; | 68 int32 times_mapped; |
45 }; | 69 }; |
46 | 70 |
47 struct MemoryMap { | 71 struct MemoryMap { |
48 std::string name; | 72 std::string name; |
49 std::string flags; | 73 std::string flags; |
50 uint start_address; | 74 uint start_address; |
51 uint end_address; | 75 uint end_address; |
76 uint offset; | |
52 int private_count; | 77 int private_count; |
53 int unevictable_private_count; | 78 int unevictable_private_count; |
54 int other_shared_count; | 79 int other_shared_count; |
55 int unevictable_other_shared_count; | 80 int unevictable_other_shared_count; |
56 // app_shared_counts[i] contains the number of pages mapped in i+2 processes | 81 // app_shared_counts[i] contains the number of pages mapped in i+2 processes |
57 // (only among the processes that are being analyzed). | 82 // (only among the processes that are being analyzed). |
58 std::vector<int> app_shared_counts; | 83 std::vector<int> app_shared_counts; |
59 std::vector<PageInfo> committed_pages; | 84 std::vector<PageInfo> committed_pages; |
85 // committed_pages_bits is a bitmap reflecting the present bit for all the | |
Philippe
2013/07/22 09:20:55
Nit: s/bitmap/bitset maybe.
Primiano Tucci (use gerrit)
2013/07/22 23:22:35
Done.
| |
86 // virtual pages of the mapping. | |
87 BitSet committed_pages_bits; | |
60 }; | 88 }; |
61 | 89 |
62 struct ProcessMemory { | 90 struct ProcessMemory { |
63 pid_t pid; | 91 pid_t pid; |
64 std::vector<MemoryMap> memory_maps; | 92 std::vector<MemoryMap> memory_maps; |
65 }; | 93 }; |
66 | 94 |
67 bool PageIsUnevictable(const PageInfo& page_info) { | 95 bool PageIsUnevictable(const PageInfo& page_info) { |
68 // These constants are taken from kernel-page-flags.h. | 96 // These constants are taken from kernel-page-flags.h. |
69 const int KPF_DIRTY = 4; // Note that only file-mapped pages can be DIRTY. | 97 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( | 135 base::StringPiece( |
108 addr_range.begin() + end_addr_start_pos, | 136 addr_range.begin() + end_addr_start_pos, |
109 addr_range.begin() + end_addr_start_pos + addr_len), | 137 addr_range.begin() + end_addr_start_pos + addr_len), |
110 &tmp)) { | 138 &tmp)) { |
111 return false; | 139 return false; |
112 } | 140 } |
113 memory_map->end_address = static_cast<uint>(tmp); | 141 memory_map->end_address = static_cast<uint>(tmp); |
114 if (tokens->at(1).size() != strlen("rwxp")) | 142 if (tokens->at(1).size() != strlen("rwxp")) |
115 return false; | 143 return false; |
116 memory_map->flags.swap(tokens->at(1)); | 144 memory_map->flags.swap(tokens->at(1)); |
145 if (!base::HexStringToUInt64(tokens->at(2), &tmp)) | |
146 return false; | |
147 memory_map->offset = static_cast<uint>(tmp); | |
148 memory_map->committed_pages_bits.setSize( | |
149 (memory_map->end_address - memory_map->start_address) / PAGE_SIZE); | |
117 const int map_name_index = 5; | 150 const int map_name_index = 5; |
118 if (tokens->size() >= map_name_index + 1) { | 151 if (tokens->size() >= map_name_index + 1) { |
119 for (std::vector<std::string>::const_iterator it = | 152 for (std::vector<std::string>::const_iterator it = |
120 tokens->begin() + map_name_index; it != tokens->end(); ++it) { | 153 tokens->begin() + map_name_index; it != tokens->end(); ++it) { |
121 if (!it->empty()) { | 154 if (!it->empty()) { |
122 if (!memory_map->name.empty()) | 155 if (!memory_map->name.empty()) |
123 memory_map->name.append(" "); | 156 memory_map->name.append(" "); |
124 memory_map->name.append(*it); | 157 memory_map->name.append(*it); |
125 } | 158 } |
126 } | 159 } |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
160 } | 193 } |
161 process_maps->push_back(memory_map); | 194 process_maps->push_back(memory_map); |
162 } | 195 } |
163 return true; | 196 return true; |
164 } | 197 } |
165 | 198 |
166 // Fills |committed_pages| in with the set of committed pages contained in the | 199 // Fills |committed_pages| in with the set of committed pages contained in the |
167 // provided memory map. | 200 // provided memory map. |
168 bool GetPagesForMemoryMap(int pagemap_fd, | 201 bool GetPagesForMemoryMap(int pagemap_fd, |
169 const MemoryMap& memory_map, | 202 const MemoryMap& memory_map, |
170 std::vector<PageInfo>* committed_pages) { | 203 std::vector<PageInfo>* committed_pages, |
171 for (uint addr = memory_map.start_address; addr < memory_map.end_address; | 204 BitSet* committed_pages_bits) { |
172 addr += PAGE_SIZE) { | 205 for (uint addr = memory_map.start_address, page_index = 0; |
206 addr < memory_map.end_address; | |
207 addr += PAGE_SIZE, ++page_index) { | |
173 DCHECK_EQ(0, addr % PAGE_SIZE); | 208 DCHECK_EQ(0, addr % PAGE_SIZE); |
174 PageMapEntry page_map_entry = {}; | 209 PageMapEntry page_map_entry = {}; |
175 COMPILE_ASSERT(sizeof(PageMapEntry) == sizeof(uint64), unexpected_size); | 210 COMPILE_ASSERT(sizeof(PageMapEntry) == sizeof(uint64), unexpected_size); |
176 const off64_t offset = addr / PAGE_SIZE; | 211 const off64_t offset = addr / PAGE_SIZE; |
177 if (!ReadFromFileAtOffset(pagemap_fd, offset, &page_map_entry)) | 212 if (!ReadFromFileAtOffset(pagemap_fd, offset, &page_map_entry)) |
178 return false; | 213 return false; |
179 if (page_map_entry.present) { // Ignore non-committed pages. | 214 if (page_map_entry.present) { // Ignore non-committed pages. |
180 if (page_map_entry.page_frame_number == 0) | 215 if (page_map_entry.page_frame_number == 0) |
181 continue; | 216 continue; |
182 PageInfo page_info = {}; | 217 PageInfo page_info = {}; |
183 page_info.page_frame_number = page_map_entry.page_frame_number; | 218 page_info.page_frame_number = page_map_entry.page_frame_number; |
184 committed_pages->push_back(page_info); | 219 committed_pages->push_back(page_info); |
220 committed_pages_bits->set(page_index); | |
185 } | 221 } |
186 } | 222 } |
187 return true; | 223 return true; |
188 } | 224 } |
189 | 225 |
190 // Fills |committed_pages| with mapping count and flags information gathered | 226 // Fills |committed_pages| with mapping count and flags information gathered |
191 // looking-up /proc/kpagecount and /proc/kpageflags. | 227 // looking-up /proc/kpagecount and /proc/kpageflags. |
192 bool SetPagesInfo(int pagecount_fd, | 228 bool SetPagesInfo(int pagecount_fd, |
193 int pageflags_fd, | 229 int pageflags_fd, |
194 std::vector<PageInfo>* pages) { | 230 std::vector<PageInfo>* pages) { |
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
380 base::SStringPrintf( | 416 base::SStringPrintf( |
381 &buf, "%d\t%d\t\t%d\t\t%d\n", | 417 &buf, "%d\t%d\t\t%d\t\t%d\n", |
382 process_memory.pid, | 418 process_memory.pid, |
383 total_private * KB_PER_PAGE, | 419 total_private * KB_PER_PAGE, |
384 static_cast<int>(total_app_shared) * KB_PER_PAGE, | 420 static_cast<int>(total_app_shared) * KB_PER_PAGE, |
385 total_other_shared * KB_PER_PAGE); | 421 total_other_shared * KB_PER_PAGE); |
386 std::cout << buf; | 422 std::cout << buf; |
387 } | 423 } |
388 } | 424 } |
389 | 425 |
426 void DumpProcessesMemoryMapsInExtendedFormat( | |
427 const std::vector<ProcessMemory>& processes_memory) { | |
428 std::string buf; | |
429 std::string app_shared_buf; | |
430 for (std::vector<ProcessMemory>::const_iterator it = processes_memory.begin(); | |
431 it != processes_memory.end(); ++it) { | |
432 const ProcessMemory& process_memory = *it; | |
433 std::cout << "[ PID=" << process_memory.pid << "]" << '\n'; | |
434 const std::vector<MemoryMap>& memory_maps = process_memory.memory_maps; | |
435 for (std::vector<MemoryMap>::const_iterator it = memory_maps.begin(); | |
436 it != memory_maps.end(); ++it) { | |
437 const MemoryMap& memory_map = *it; | |
438 app_shared_buf.clear(); | |
439 AppendAppSharedField(memory_map.app_shared_counts, &app_shared_buf); | |
440 std::string pages_bits(memory_map.committed_pages_bits.getData(), | |
441 memory_map.committed_pages_bits.getLength()); | |
442 std::string encoded_page_bits; | |
443 base::Base64Encode(pages_bits, &encoded_page_bits); | |
444 base::SStringPrintf( | |
445 &buf, | |
446 "%x-%x %s %x private_unevictable=%d private=%d shared_app=%s " | |
447 "shared_other_unevictable=%d shared_other=%d \"%s\" [%s]\n", | |
448 memory_map.start_address, | |
449 memory_map.end_address, | |
450 memory_map.flags.c_str(), | |
451 memory_map.offset, | |
452 memory_map.unevictable_private_count * PAGE_SIZE, | |
453 memory_map.private_count * PAGE_SIZE, | |
454 app_shared_buf.c_str(), | |
455 memory_map.unevictable_other_shared_count * PAGE_SIZE, | |
456 memory_map.other_shared_count * PAGE_SIZE, | |
457 memory_map.name.c_str(), | |
458 encoded_page_bits.c_str()); | |
459 std::cout << buf; | |
460 } | |
461 } | |
462 } | |
463 | |
390 bool CollectProcessMemoryInformation(int page_count_fd, | 464 bool CollectProcessMemoryInformation(int page_count_fd, |
391 int page_flags_fd, | 465 int page_flags_fd, |
392 ProcessMemory* process_memory) { | 466 ProcessMemory* process_memory) { |
393 const pid_t pid = process_memory->pid; | 467 const pid_t pid = process_memory->pid; |
394 int pagemap_fd = open( | 468 int pagemap_fd = open( |
395 base::StringPrintf("/proc/%d/pagemap", pid).c_str(), O_RDONLY); | 469 base::StringPrintf("/proc/%d/pagemap", pid).c_str(), O_RDONLY); |
396 if (pagemap_fd < 0) { | 470 if (pagemap_fd < 0) { |
397 PLOG(ERROR) << "open"; | 471 PLOG(ERROR) << "open"; |
398 return false; | 472 return false; |
399 } | 473 } |
400 file_util::ScopedFD auto_closer(&pagemap_fd); | 474 file_util::ScopedFD auto_closer(&pagemap_fd); |
401 std::vector<MemoryMap>* const process_maps = &process_memory->memory_maps; | 475 std::vector<MemoryMap>* const process_maps = &process_memory->memory_maps; |
402 if (!GetProcessMaps(pid, process_maps)) | 476 if (!GetProcessMaps(pid, process_maps)) |
403 return false; | 477 return false; |
404 for (std::vector<MemoryMap>::iterator it = process_maps->begin(); | 478 for (std::vector<MemoryMap>::iterator it = process_maps->begin(); |
405 it != process_maps->end(); ++it) { | 479 it != process_maps->end(); ++it) { |
406 std::vector<PageInfo>* const committed_pages = &it->committed_pages; | 480 std::vector<PageInfo>* const committed_pages = &it->committed_pages; |
407 GetPagesForMemoryMap(pagemap_fd, *it, committed_pages); | 481 BitSet* pages_bits = &it->committed_pages_bits; |
Philippe
2013/07/22 09:20:55
Nit: 'BitSet* const pages_bits' maybe for consiste
Primiano Tucci (use gerrit)
2013/07/22 23:22:35
Done.
| |
482 GetPagesForMemoryMap(pagemap_fd, *it, committed_pages, pages_bits); | |
408 SetPagesInfo(page_count_fd, page_flags_fd, committed_pages); | 483 SetPagesInfo(page_count_fd, page_flags_fd, committed_pages); |
409 } | 484 } |
410 return true; | 485 return true; |
411 } | 486 } |
412 | 487 |
413 void KillAll(const std::vector<pid_t>& pids, int signal_number) { | 488 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(); | 489 for (std::vector<pid_t>::const_iterator it = pids.begin(); it != pids.end(); |
415 ++it) { | 490 ++it) { |
416 kill(*it, signal_number); | 491 kill(*it, signal_number); |
417 } | 492 } |
418 } | 493 } |
419 | 494 |
420 } // namespace | 495 } // namespace |
421 | 496 |
422 int main(int argc, char** argv) { | 497 int main(int argc, char** argv) { |
423 bool short_output = false; | |
424 if (argc == 1) { | 498 if (argc == 1) { |
425 LOG(ERROR) << "Usage: " << argv[0] << " [-a] <PID1>... <PIDN>"; | 499 LOG(ERROR) << "Usage: " << argv[0] << " [-a|-x] <PID1>... <PIDN>"; |
426 return EXIT_FAILURE; | 500 return EXIT_FAILURE; |
427 } | 501 } |
428 if (!strncmp(argv[1], "-a", 2)) { | 502 const bool short_output = !strncmp(argv[1], "-a", 2); |
503 const bool extended_output = !strncmp(argv[1], "-x", 2); | |
504 if (short_output || extended_output) { | |
429 if (argc == 2) { | 505 if (argc == 2) { |
430 LOG(ERROR) << "Usage: " << argv[0] << " [-a] <PID1>... <PIDN>"; | 506 LOG(ERROR) << "Usage: " << argv[0] << " [-a|-x] <PID1>... <PIDN>"; |
431 return EXIT_FAILURE; | 507 return EXIT_FAILURE; |
432 } | 508 } |
433 short_output = true; | |
434 ++argv; | 509 ++argv; |
435 } | 510 } |
436 std::vector<pid_t> pids; | 511 std::vector<pid_t> pids; |
437 for (const char* const* ptr = argv + 1; *ptr; ++ptr) { | 512 for (const char* const* ptr = argv + 1; *ptr; ++ptr) { |
438 pid_t pid; | 513 pid_t pid; |
439 if (!base::StringToInt(*ptr, &pid)) | 514 if (!base::StringToInt(*ptr, &pid)) |
440 return EXIT_FAILURE; | 515 return EXIT_FAILURE; |
441 pids.push_back(pid); | 516 pids.push_back(pid); |
442 } | 517 } |
443 | 518 |
(...skipping 25 matching lines...) Expand all Loading... | |
469 if (!CollectProcessMemoryInformation(page_count_fd, | 544 if (!CollectProcessMemoryInformation(page_count_fd, |
470 page_flags_fd, | 545 page_flags_fd, |
471 process_memory)) | 546 process_memory)) |
472 return EXIT_FAILURE; | 547 return EXIT_FAILURE; |
473 } | 548 } |
474 } | 549 } |
475 | 550 |
476 ClassifyPages(&processes_memory); | 551 ClassifyPages(&processes_memory); |
477 if (short_output) | 552 if (short_output) |
478 DumpProcessesMemoryMapsInShortFormat(processes_memory); | 553 DumpProcessesMemoryMapsInShortFormat(processes_memory); |
554 else if (extended_output) | |
555 DumpProcessesMemoryMapsInExtendedFormat(processes_memory); | |
479 else | 556 else |
480 DumpProcessesMemoryMaps(processes_memory); | 557 DumpProcessesMemoryMaps(processes_memory); |
481 return EXIT_SUCCESS; | 558 return EXIT_SUCCESS; |
482 } | 559 } |
OLD | NEW |