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
3 // found in the LICENSE file.
19 #include "base/base64.h"
20 #include "base/basictypes.h"
21 #include "base/bind.h"
22 #include "base/callback_helpers.h"
23 #include "base/containers/hash_tables.h"
24 #include "base/file_util.h"
25 #include "base/logging.h"
26 #include "base/memory/scoped_ptr.h"
27 #include "base/strings/string_number_conversions.h"
28 #include "base/strings/string_piece.h"
29 #include "base/strings/string_split.h"
30 #include "base/strings/stringprintf.h"
36 void resize(size_t nbits) {
37 data_.resize((nbits + 7) / 8);
40 void set(uint32 bit) {
41 const uint32 byte_idx = bit / 8;
42 CHECK(byte_idx < data_.size());
43 data_[byte_idx] |= (1 << (bit & 7));
46 std::string AsB64String() const {
47 std::string bits(&data_[0], data_.size());
48 std::string b64_string;
49 base::Base64Encode(bits, &b64_string);
54 std::vector<char> data_;
57 // An entry in /proc/<pid>/pagemap.
59 uint64 page_frame_number : 55;
64 // Describes a memory page.
66 int64 page_frame_number; // Physical page id, also known as PFN.
72 PageCount() : total_count(0), unevictable_count(0) {}
75 int unevictable_count;
84 PageCount private_pages;
85 // app_shared_pages[i] contains the number of pages mapped in i+2 processes
86 // (only among the processes that are being analyzed).
87 std::vector<PageCount> app_shared_pages;
88 PageCount other_shared_pages;
89 std::vector<PageInfo> committed_pages;
90 // committed_pages_bits is a bitset reflecting the present bit for all the
91 // virtual pages of the mapping.
92 BitSet committed_pages_bits;
95 struct ProcessMemory {
97 std::vector<MemoryMap> memory_maps;
100 bool PageIsUnevictable(const PageInfo& page_info) {
101 // These constants are taken from kernel-page-flags.h.
102 const int KPF_DIRTY = 4; // Note that only file-mapped pages can be DIRTY.
103 const int KPF_ANON = 12; // Anonymous pages are dirty per definition.
104 const int KPF_UNEVICTABLE = 18;
105 const int KPF_MLOCKED = 33;
107 return (page_info.flags & ((1ll << KPF_DIRTY) |
109 (1ll << KPF_UNEVICTABLE) |
110 (1ll << KPF_MLOCKED))) ?
114 // Number of times a physical page is mapped in a process.
115 typedef base::hash_map<uint64, int> PFNMap;
117 // Parses lines from /proc/<PID>/maps, e.g.:
118 // 401e7000-401f5000 r-xp 00000000 103:02 158 /system/bin/linker
119 bool ParseMemoryMapLine(const std::string& line,
120 std::vector<std::string>* tokens,
121 MemoryMap* memory_map) {
123 base::SplitString(line, ' ', tokens);
124 if (tokens->size() < 2)
126 const int addr_len = 8;
127 const std::string& addr_range = tokens->at(0);
128 if (addr_range.length() != addr_len + 1 + addr_len)
131 if (!base::HexStringToUInt64(
133 addr_range.begin(), addr_range.begin() + addr_len),
137 memory_map->start_address = static_cast<uint>(tmp);
138 const int end_addr_start_pos = addr_len + 1;
139 if (!base::HexStringToUInt64(
141 addr_range.begin() + end_addr_start_pos,
142 addr_range.begin() + end_addr_start_pos + addr_len),
146 memory_map->end_address = static_cast<uint>(tmp);
147 if (tokens->at(1).size() != strlen("rwxp"))
149 memory_map->flags.swap(tokens->at(1));
150 if (!base::HexStringToUInt64(tokens->at(2), &tmp))
152 memory_map->offset = static_cast<uint>(tmp);
153 memory_map->committed_pages_bits.resize(
154 (memory_map->end_address - memory_map->start_address) / PAGE_SIZE);
155 const int map_name_index = 5;
156 if (tokens->size() >= map_name_index + 1) {
157 for (std::vector<std::string>::const_iterator it =
158 tokens->begin() + map_name_index; it != tokens->end(); ++it) {
160 if (!memory_map->name.empty())
161 memory_map->name.append(" ");
162 memory_map->name.append(*it);
169 // Reads sizeof(T) bytes from file |fd| at |offset|.
170 template <typename T>
171 bool ReadFromFileAtOffset(int fd, off_t offset, T* value) {
172 if (lseek64(fd, offset * sizeof(*value), SEEK_SET) < 0) {
173 PLOG(ERROR) << "lseek";
176 ssize_t bytes = read(fd, value, sizeof(*value));
177 if (bytes != sizeof(*value) && bytes != 0) {
178 PLOG(ERROR) << "read";
184 // Fills |process_maps| in with the process memory maps identified by |pid|.
185 bool GetProcessMaps(pid_t pid, std::vector<MemoryMap>* process_maps) {
186 std::ifstream maps_file(base::StringPrintf("/proc/%d/maps", pid).c_str());
187 if (!maps_file.good()) {
188 PLOG(ERROR) << "open";
192 std::vector<std::string> tokens;
193 while (std::getline(maps_file, line) && !line.empty()) {
194 MemoryMap memory_map = {};
195 if (!ParseMemoryMapLine(line, &tokens, &memory_map)) {
196 LOG(ERROR) << "Could not parse line: " << line;
199 process_maps->push_back(memory_map);
204 // Fills |committed_pages| in with the set of committed pages contained in the
205 // provided memory map.
206 bool GetPagesForMemoryMap(int pagemap_fd,
207 const MemoryMap& memory_map,
208 std::vector<PageInfo>* committed_pages,
209 BitSet* committed_pages_bits) {
210 for (uint addr = memory_map.start_address, page_index = 0;
211 addr < memory_map.end_address;
212 addr += PAGE_SIZE, ++page_index) {
213 DCHECK_EQ(0, addr % PAGE_SIZE);
214 PageMapEntry page_map_entry = {};
215 COMPILE_ASSERT(sizeof(PageMapEntry) == sizeof(uint64), unexpected_size);
216 const off64_t offset = addr / PAGE_SIZE;
217 if (!ReadFromFileAtOffset(pagemap_fd, offset, &page_map_entry))
219 if (page_map_entry.present) { // Ignore non-committed pages.
220 if (page_map_entry.page_frame_number == 0)
222 PageInfo page_info = {};
223 page_info.page_frame_number = page_map_entry.page_frame_number;
224 committed_pages->push_back(page_info);
225 committed_pages_bits->set(page_index);
231 // Fills |committed_pages| with mapping count and flags information gathered
232 // looking-up /proc/kpagecount and /proc/kpageflags.
233 bool SetPagesInfo(int pagecount_fd,
235 std::vector<PageInfo>* pages) {
236 for (std::vector<PageInfo>::iterator it = pages->begin();
237 it != pages->end(); ++it) {
238 PageInfo* const page_info = &*it;
240 if (!ReadFromFileAtOffset(
241 pagecount_fd, page_info->page_frame_number, ×_mapped)) {
244 DCHECK(times_mapped <= std::numeric_limits<int32_t>::max());
245 page_info->times_mapped = static_cast<int32>(times_mapped);
248 if (!ReadFromFileAtOffset(
249 pageflags_fd, page_info->page_frame_number, &page_flags)) {
252 page_info->flags = page_flags;
257 // Fills in the provided vector of Page Frame Number maps. This lets
258 // ClassifyPages() know how many times each page is mapped in the processes.
259 void FillPFNMaps(const std::vector<ProcessMemory>& processes_memory,
260 std::vector<PFNMap>* pfn_maps) {
261 int current_process_index = 0;
262 for (std::vector<ProcessMemory>::const_iterator it = processes_memory.begin();
263 it != processes_memory.end(); ++it, ++current_process_index) {
264 const std::vector<MemoryMap>& memory_maps = it->memory_maps;
265 for (std::vector<MemoryMap>::const_iterator it = memory_maps.begin();
266 it != memory_maps.end(); ++it) {
267 const std::vector<PageInfo>& pages = it->committed_pages;
268 for (std::vector<PageInfo>::const_iterator it = pages.begin();
269 it != pages.end(); ++it) {
270 const PageInfo& page_info = *it;
271 PFNMap* const pfn_map = &(*pfn_maps)[current_process_index];
272 const std::pair<PFNMap::iterator, bool> result = pfn_map->insert(
273 std::make_pair(page_info.page_frame_number, 0));
274 ++result.first->second;
280 // Sets the private_count/app_shared_pages/other_shared_count fields of the
281 // provided memory maps for each process.
282 void ClassifyPages(std::vector<ProcessMemory>* processes_memory) {
283 std::vector<PFNMap> pfn_maps(processes_memory->size());
284 FillPFNMaps(*processes_memory, &pfn_maps);
285 // Hash set keeping track of the physical pages mapped in a single process so
286 // that they can be counted only once.
287 std::hash_set<uint64> physical_pages_mapped_in_process;
289 for (std::vector<ProcessMemory>::iterator it = processes_memory->begin();
290 it != processes_memory->end(); ++it) {
291 std::vector<MemoryMap>* const memory_maps = &it->memory_maps;
292 physical_pages_mapped_in_process.clear();
293 for (std::vector<MemoryMap>::iterator it = memory_maps->begin();
294 it != memory_maps->end(); ++it) {
295 MemoryMap* const memory_map = &*it;
296 const size_t processes_count = processes_memory->size();
297 memory_map->app_shared_pages.resize(processes_count - 1);
298 const std::vector<PageInfo>& pages = memory_map->committed_pages;
299 for (std::vector<PageInfo>::const_iterator it = pages.begin();
300 it != pages.end(); ++it) {
301 const PageInfo& page_info = *it;
302 if (page_info.times_mapped == 1) {
303 ++memory_map->private_pages.total_count;
304 if (PageIsUnevictable(page_info))
305 ++memory_map->private_pages.unevictable_count;
308 const uint64 page_frame_number = page_info.page_frame_number;
309 const std::pair<std::hash_set<uint64>::iterator, bool> result =
310 physical_pages_mapped_in_process.insert(page_frame_number);
311 const bool did_insert = result.second;
313 // This physical page (mapped multiple times in the same process) was
317 // See if the current physical page is also mapped in the other
318 // processes that are being analyzed.
319 int times_mapped = 0;
320 int mapped_in_processes_count = 0;
321 for (std::vector<PFNMap>::const_iterator it = pfn_maps.begin();
322 it != pfn_maps.end(); ++it) {
323 const PFNMap& pfn_map = *it;
324 const PFNMap::const_iterator found_it = pfn_map.find(
326 if (found_it == pfn_map.end())
328 ++mapped_in_processes_count;
329 times_mapped += found_it->second;
331 PageCount* page_count_to_update = NULL;
332 if (times_mapped == page_info.times_mapped) {
333 // The physical page is only mapped in the processes that are being
335 if (mapped_in_processes_count > 1) {
336 // The physical page is mapped in multiple processes.
337 page_count_to_update =
338 &memory_map->app_shared_pages[mapped_in_processes_count - 2];
340 // The physical page is mapped multiple times in the same process.
341 page_count_to_update = &memory_map->private_pages;
344 page_count_to_update = &memory_map->other_shared_pages;
346 ++page_count_to_update->total_count;
347 if (PageIsUnevictable(page_info))
348 ++page_count_to_update->unevictable_count;
354 void AppendAppSharedField(const std::vector<PageCount>& app_shared_pages,
357 for (std::vector<PageCount>::const_iterator it = app_shared_pages.begin();
358 it != app_shared_pages.end(); ++it) {
359 out->append(base::IntToString(it->total_count * PAGE_SIZE));
361 out->append(base::IntToString(it->unevictable_count * PAGE_SIZE));
362 if (it + 1 != app_shared_pages.end())
368 void DumpProcessesMemoryMapsInShortFormat(
369 const std::vector<ProcessMemory>& processes_memory) {
370 const int KB_PER_PAGE = PAGE_SIZE >> 10;
371 std::vector<int> totals_app_shared(processes_memory.size());
373 std::cout << "pid\tprivate\t\tshared_app\tshared_other (KB)\n";
374 for (std::vector<ProcessMemory>::const_iterator it = processes_memory.begin();
375 it != processes_memory.end(); ++it) {
376 const ProcessMemory& process_memory = *it;
377 std::fill(totals_app_shared.begin(), totals_app_shared.end(), 0);
378 int total_private = 0, total_other_shared = 0;
379 const std::vector<MemoryMap>& memory_maps = process_memory.memory_maps;
380 for (std::vector<MemoryMap>::const_iterator it = memory_maps.begin();
381 it != memory_maps.end(); ++it) {
382 const MemoryMap& memory_map = *it;
383 total_private += memory_map.private_pages.total_count;
384 for (size_t i = 0; i < memory_map.app_shared_pages.size(); ++i)
385 totals_app_shared[i] += memory_map.app_shared_pages[i].total_count;
386 total_other_shared += memory_map.other_shared_pages.total_count;
388 double total_app_shared = 0;
389 for (size_t i = 0; i < totals_app_shared.size(); ++i)
390 total_app_shared += static_cast<double>(totals_app_shared[i]) / (i + 2);
392 &buf, "%d\t%d\t\t%d\t\t%d\n",
394 total_private * KB_PER_PAGE,
395 static_cast<int>(total_app_shared) * KB_PER_PAGE,
396 total_other_shared * KB_PER_PAGE);
401 void DumpProcessesMemoryMapsInExtendedFormat(
402 const std::vector<ProcessMemory>& processes_memory) {
404 std::string app_shared_buf;
405 for (std::vector<ProcessMemory>::const_iterator it = processes_memory.begin();
406 it != processes_memory.end(); ++it) {
407 const ProcessMemory& process_memory = *it;
408 std::cout << "[ PID=" << process_memory.pid << "]" << '\n';
409 const std::vector<MemoryMap>& memory_maps = process_memory.memory_maps;
410 for (std::vector<MemoryMap>::const_iterator it = memory_maps.begin();
411 it != memory_maps.end(); ++it) {
412 const MemoryMap& memory_map = *it;
413 app_shared_buf.clear();
414 AppendAppSharedField(memory_map.app_shared_pages, &app_shared_buf);
417 "%x-%x %s %x private_unevictable=%d private=%d shared_app=%s "
418 "shared_other_unevictable=%d shared_other=%d \"%s\" [%s]\n",
419 memory_map.start_address,
420 memory_map.end_address,
421 memory_map.flags.c_str(),
423 memory_map.private_pages.unevictable_count * PAGE_SIZE,
424 memory_map.private_pages.total_count * PAGE_SIZE,
425 app_shared_buf.c_str(),
426 memory_map.other_shared_pages.unevictable_count * PAGE_SIZE,
427 memory_map.other_shared_pages.total_count * PAGE_SIZE,
428 memory_map.name.c_str(),
429 memory_map.committed_pages_bits.AsB64String().c_str());
435 bool CollectProcessMemoryInformation(int page_count_fd,
437 ProcessMemory* process_memory) {
438 const pid_t pid = process_memory->pid;
439 int pagemap_fd = open(
440 base::StringPrintf("/proc/%d/pagemap", pid).c_str(), O_RDONLY);
441 if (pagemap_fd < 0) {
442 PLOG(ERROR) << "open";
445 file_util::ScopedFD auto_closer(&pagemap_fd);
446 std::vector<MemoryMap>* const process_maps = &process_memory->memory_maps;
447 if (!GetProcessMaps(pid, process_maps))
449 for (std::vector<MemoryMap>::iterator it = process_maps->begin();
450 it != process_maps->end(); ++it) {
451 std::vector<PageInfo>* const committed_pages = &it->committed_pages;
452 BitSet* const pages_bits = &it->committed_pages_bits;
453 GetPagesForMemoryMap(pagemap_fd, *it, committed_pages, pages_bits);
454 SetPagesInfo(page_count_fd, page_flags_fd, committed_pages);
459 void KillAll(const std::vector<pid_t>& pids, int signal_number) {
460 for (std::vector<pid_t>::const_iterator it = pids.begin(); it != pids.end();
462 kill(*it, signal_number);
466 void ExitWithUsage() {
467 LOG(ERROR) << "Usage: memdump [-a] <PID1>... <PIDN>";
473 int main(int argc, char** argv) {
476 const bool short_output = !strncmp(argv[1], "-a", 2);
482 std::vector<pid_t> pids;
483 for (const char* const* ptr = argv + 1; *ptr; ++ptr) {
485 if (!base::StringToInt(*ptr, &pid))
490 std::vector<ProcessMemory> processes_memory(pids.size());
492 int page_count_fd = open("/proc/kpagecount", O_RDONLY);
493 if (page_count_fd < 0) {
494 PLOG(ERROR) << "open /proc/kpagecount";
498 int page_flags_fd = open("/proc/kpageflags", O_RDONLY);
499 if (page_flags_fd < 0) {
500 PLOG(ERROR) << "open /proc/kpageflags";
504 file_util::ScopedFD page_count_fd_closer(&page_count_fd);
505 file_util::ScopedFD page_flags_fd_closer(&page_flags_fd);
507 base::ScopedClosureRunner auto_resume_processes(
508 base::Bind(&KillAll, pids, SIGCONT));
509 KillAll(pids, SIGSTOP);
510 for (std::vector<pid_t>::const_iterator it = pids.begin(); it != pids.end();
512 ProcessMemory* const process_memory =
513 &processes_memory[it - pids.begin()];
514 process_memory->pid = *it;
515 if (!CollectProcessMemoryInformation(
516 page_count_fd, page_flags_fd, process_memory)) {
522 ClassifyPages(&processes_memory);
524 DumpProcessesMemoryMapsInShortFormat(processes_memory);
526 DumpProcessesMemoryMapsInExtendedFormat(processes_memory);