Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(907)

Side by Side Diff: tools/android/memdump/memdump.cc

Issue 15861023: Add tools/android/memdump/. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Update license Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « tools/android/android_tools.gyp ('k') | tools/android/memdump/memdump.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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.
4
5 #include <fcntl.h>
6 #include <signal.h>
7 #include <sys/types.h>
8 #include <unistd.h>
9
10 #include <algorithm>
11 #include <cstring>
12 #include <fstream>
13 #include <iostream>
14 #include <string>
15 #include <utility>
16 #include <vector>
17
18 #include "base/basictypes.h"
19 #include "base/bind.h"
20 #include "base/bind_helpers.h"
21 #include "base/file_util.h"
22 #include "base/hash_tables.h"
23 #include "base/logging.h"
24 #include "base/memory/scoped_ptr.h"
25 #include "base/string_number_conversions.h"
26 #include "base/stringprintf.h"
27 #include "base/strings/string_piece.h"
28 #include "base/strings/string_split.h"
29
30 namespace {
31
32 // An entry in /proc/<pid>/pagemap.
33 struct PageMapEntry {
34 uint64 page_frame_number : 55;
35 uint unused : 8;
36 uint present : 1;
37 };
38
39 // Describes a virtual page.
40 struct PageInfo {
41 int64 page_frame_number : 55; // Physical page id, also known as PFN.
42 int64 times_mapped : 9 + 32;
43 };
44
45 struct MemoryMap {
46 std::string name;
47 std::string flags;
48 uint start_address;
49 uint end_address;
50 int private_count;
51 int app_shared_count;
52 int other_shared_count;
53 std::vector<PageInfo> committed_pages;
54 };
55
56 struct ProcessMemory {
57 pid_t pid;
58 std::vector<MemoryMap> memory_maps;
59 };
60
61 // Number of times a physical page is mapped in a process.
62 typedef base::hash_map<uint64, int> PFNMap;
63
64 // Parses lines from /proc/<PID>/maps, e.g.:
65 // 401e7000-401f5000 r-xp 00000000 103:02 158 /system/bin/linker
66 bool ParseMemoryMapLine(const std::string& line,
67 std::vector<std::string>* tokens,
68 MemoryMap* memory_map) {
69 tokens->clear();
70 base::SplitString(line, ' ', tokens);
71 if (tokens->size() < 2)
72 return false;
73 const int addr_len = 8;
74 const std::string& addr_range = tokens->at(0);
75 if (addr_range.length() != addr_len + 1 + addr_len)
76 return false;
77 uint64 tmp = 0;
78 if (!base::HexStringToUInt64(
79 base::StringPiece(
80 addr_range.begin(), addr_range.begin() + addr_len),
81 &tmp)) {
82 return false;
83 }
84 memory_map->start_address = static_cast<uint>(tmp);
85 const int end_addr_start_pos = addr_len + 1;
86 if (!base::HexStringToUInt64(
87 base::StringPiece(
88 addr_range.begin() + end_addr_start_pos,
89 addr_range.begin() + end_addr_start_pos + addr_len),
90 &tmp)) {
91 return false;
92 }
93 memory_map->end_address = static_cast<uint>(tmp);
94 if (tokens->at(1).size() != strlen("rwxp"))
95 return false;
96 memory_map->flags.swap(tokens->at(1));
97 const int map_name_index = 5;
98 if (tokens->size() >= map_name_index + 1) {
99 for (std::vector<std::string>::const_iterator it =
100 tokens->begin() + map_name_index; it != tokens->end(); ++it) {
101 if (!it->empty()) {
102 if (!memory_map->name.empty())
103 memory_map->name.append(" ");
104 memory_map->name.append(*it);
105 }
106 }
107 }
108 return true;
109 }
110
111 // Reads sizeof(T) bytes from file |fd| at |offset|.
112 template <typename T>
113 bool ReadFromFileAtOffset(int fd, off_t offset, T* value) {
114 if (lseek64(fd, offset * sizeof(*value), SEEK_SET) < 0) {
115 PLOG(ERROR) << "lseek";
116 return false;
117 }
118 ssize_t bytes = read(fd, value, sizeof(*value));
119 if (bytes != sizeof(*value) && bytes != 0) {
120 PLOG(ERROR) << "read";
121 return false;
122 }
123 return true;
124 }
125
126 // Fills |process_maps| in with the process memory maps identified by |pid|.
127 bool GetProcessMaps(pid_t pid, std::vector<MemoryMap>* process_maps) {
128 std::ifstream maps_file(base::StringPrintf("/proc/%d/maps", pid).c_str());
129 if (!maps_file.good()) {
130 PLOG(ERROR) << "open";
131 return false;
132 }
133 std::string line;
134 std::vector<std::string> tokens;
135 while (std::getline(maps_file, line) && !line.empty()) {
136 MemoryMap memory_map = {};
137 if (!ParseMemoryMapLine(line, &tokens, &memory_map)) {
138 LOG(ERROR) << "Could not parse line: " << line;
139 return false;
140 }
141 process_maps->push_back(memory_map);
142 }
143 return true;
144 }
145
146 // Fills |committed_pages| in with the set of committed pages contained in the
147 // provided memory map.
148 bool GetPagesForMemoryMap(int pagemap_fd,
149 const MemoryMap& memory_map,
150 std::vector<PageInfo>* committed_pages) {
151 for (uint addr = memory_map.start_address; addr < memory_map.end_address;
152 addr += PAGE_SIZE) {
153 DCHECK_EQ(0, addr % PAGE_SIZE);
154 PageMapEntry page_map_entry = {};
155 COMPILE_ASSERT(sizeof(PageMapEntry) == sizeof(uint64), unexpected_size);
156 const off64_t offset = addr / PAGE_SIZE;
157 if (!ReadFromFileAtOffset(pagemap_fd, offset, &page_map_entry))
158 return false;
159 if (page_map_entry.present) { // Ignore non-committed pages.
160 if (page_map_entry.page_frame_number == 0)
161 continue;
162 PageInfo page_info = {};
163 page_info.page_frame_number = page_map_entry.page_frame_number;
164 committed_pages->push_back(page_info);
165 }
166 }
167 return true;
168 }
169
170 bool SetTimesMapped(int pagecount_fd, std::vector<PageInfo>* pages) {
171 for (std::vector<PageInfo>::iterator it = pages->begin();
172 it != pages->end(); ++it) {
173 PageInfo* const page_info = &*it;
174 int64 times_mapped;
175 if (!ReadFromFileAtOffset(
176 pagecount_fd, page_info->page_frame_number, &times_mapped)) {
177 return false;
178 }
179 page_info->times_mapped = times_mapped;
180 }
181 return true;
182 }
183
184 // Fills in the provided vector of Page Frame Number maps. This lets
185 // ClassifyPages() know how many times each page is mapped in the processes.
186 void FillPFNMaps(const std::vector<ProcessMemory>& processes_memory,
187 std::vector<PFNMap>* pfn_maps) {
188 int current_process_index = 0;
189 for (std::vector<ProcessMemory>::const_iterator it = processes_memory.begin();
190 it != processes_memory.end(); ++it, ++current_process_index) {
191 const std::vector<MemoryMap>& memory_maps = it->memory_maps;
192 for (std::vector<MemoryMap>::const_iterator it = memory_maps.begin();
193 it != memory_maps.end(); ++it) {
194 const std::vector<PageInfo>& pages = it->committed_pages;
195 for (std::vector<PageInfo>::const_iterator it = pages.begin();
196 it != pages.end(); ++it) {
197 const PageInfo& page_info = *it;
198 PFNMap* const pfn_map = &(*pfn_maps)[current_process_index];
199 const std::pair<PFNMap::iterator, bool> result = pfn_map->insert(
200 std::make_pair(page_info.page_frame_number, 0));
201 ++result.first->second;
202 }
203 }
204 }
205 }
206
207 // Sets the private_count/app_shared_count/other_shared_count fields of the
208 // provided memory maps for each process.
209 void ClassifyPages(std::vector<ProcessMemory>* processes_memory) {
210 std::vector<PFNMap> pfn_maps(processes_memory->size());
211 FillPFNMaps(*processes_memory, &pfn_maps);
212 // Hash set keeping track of the physical pages mapped in a single process so
213 // that they can be counted only once.
214 std::hash_set<uint64> physical_pages_mapped_in_process;
215
216 for (std::vector<ProcessMemory>::iterator it = processes_memory->begin();
217 it != processes_memory->end(); ++it) {
218 std::vector<MemoryMap>* const memory_maps = &it->memory_maps;
219 physical_pages_mapped_in_process.clear();
220 for (std::vector<MemoryMap>::iterator it = memory_maps->begin();
221 it != memory_maps->end(); ++it) {
222 MemoryMap* const memory_map = &*it;
223 const std::vector<PageInfo>& pages = memory_map->committed_pages;
224 for (std::vector<PageInfo>::const_iterator it = pages.begin();
225 it != pages.end(); ++it) {
226 const PageInfo& page_info = *it;
227 if (page_info.times_mapped == 1) {
228 ++memory_map->private_count;
229 continue;
230 }
231 const uint64 page_frame_number = page_info.page_frame_number;
232 const std::pair<std::hash_set<uint64>::iterator, bool> result =
233 physical_pages_mapped_in_process.insert(page_frame_number);
234 const bool did_insert = result.second;
235 if (!did_insert) {
236 // This physical page (mapped multiple times in the same process) was
237 // already counted.
238 continue;
239 }
240 // See if the current physical page is also mapped in the other
241 // processes that are being analyzed.
242 int times_mapped = 0;
243 bool mapped_in_multiple_processes = false;
244 for (std::vector<PFNMap>::const_iterator it = pfn_maps.begin();
245 it != pfn_maps.end(); ++it) {
246 const PFNMap& pfn_map = *it;
247 const PFNMap::const_iterator found_it = pfn_map.find(
248 page_frame_number);
249 if (found_it == pfn_map.end())
250 continue;
251 if (times_mapped)
252 mapped_in_multiple_processes = true;
253 times_mapped += found_it->second;
254 }
255 if (times_mapped == page_info.times_mapped) {
256 // The physical page is only mapped in the processes that are being
257 // analyzed.
258 if (mapped_in_multiple_processes) {
259 ++memory_map->app_shared_count;
260 } else {
261 // The physical page is mapped multiple times in the same process.
262 ++memory_map->private_count;
263 }
264 } else {
265 ++memory_map->other_shared_count;
266 }
267 }
268 }
269 }
270 }
271
272 void DumpProcessesMemoryMaps(
273 const std::vector<ProcessMemory>& processes_memory) {
274 std::string buf;
275 for (std::vector<ProcessMemory>::const_iterator it = processes_memory.begin();
276 it != processes_memory.end(); ++it) {
277 const ProcessMemory& process_memory = *it;
278 std::cout << "[ PID=" << process_memory.pid << "]" << '\n';
279 const std::vector<MemoryMap>& memory_maps = process_memory.memory_maps;
280 for (std::vector<MemoryMap>::const_iterator it = memory_maps.begin();
281 it != memory_maps.end(); ++it) {
282 const MemoryMap& memory_map = *it;
283 base::SStringPrintf(
284 &buf, "%x-%x %s private=%d shared_app=%d shared_other=%d %s\n",
285 memory_map.start_address,
286 memory_map.end_address, memory_map.flags.c_str(),
287 memory_map.private_count * PAGE_SIZE,
288 memory_map.app_shared_count * PAGE_SIZE,
289 memory_map.other_shared_count * PAGE_SIZE,
290 memory_map.name.c_str());
291 std::cout << buf;
292 }
293 }
294 }
295
296 bool CollectProcessMemoryInformation(int page_count_fd,
297 ProcessMemory* process_memory) {
298 const pid_t pid = process_memory->pid;
299 int pagemap_fd = open(
300 base::StringPrintf("/proc/%d/pagemap", pid).c_str(), O_RDONLY);
301 if (pagemap_fd < 0) {
302 PLOG(ERROR) << "open";
303 return false;
304 }
305 file_util::ScopedFD auto_closer(&pagemap_fd);
306 std::vector<MemoryMap>* const process_maps = &process_memory->memory_maps;
307 if (!GetProcessMaps(pid, process_maps))
308 return false;
309 for (std::vector<MemoryMap>::iterator it = process_maps->begin();
310 it != process_maps->end(); ++it) {
311 std::vector<PageInfo>* const committed_pages = &it->committed_pages;
312 GetPagesForMemoryMap(pagemap_fd, *it, committed_pages);
313 SetTimesMapped(page_count_fd, committed_pages);
314 }
315 return true;
316 }
317
318 void KillAll(const std::vector<pid_t>& pids, int signal_number) {
319 for (std::vector<pid_t>::const_iterator it = pids.begin(); it != pids.end();
320 ++it) {
321 kill(*it, signal_number);
322 }
323 }
324
325 } // namespace
326
327 int main(int argc, char** argv) {
328 if (argc == 1) {
329 LOG(ERROR) << "Usage: " << argv[0] << " <PID1>... <PIDN>";
330 return EXIT_FAILURE;
331 }
332 std::vector<pid_t> pids;
333 for (const char* const* ptr = argv + 1; *ptr; ++ptr) {
334 pid_t pid;
335 if (!base::StringToInt(*ptr, &pid))
336 return EXIT_FAILURE;
337 pids.push_back(pid);
338 }
339 std::vector<ProcessMemory> processes_memory(pids.size());
340 {
341 int page_count_fd = open("/proc/kpagecount", O_RDONLY);
342 if (page_count_fd < 0) {
343 PLOG(ERROR) << "open";
344 return EXIT_FAILURE;
345 }
346 file_util::ScopedFD page_count_fd_closer(&page_count_fd);
347 base::ScopedClosureRunner auto_resume_processes(
348 base::Bind(&KillAll, pids, SIGCONT));
349 KillAll(pids, SIGSTOP);
350 for (std::vector<pid_t>::const_iterator it = pids.begin(); it != pids.end();
351 ++it) {
352 ProcessMemory* const process_memory =
353 &processes_memory[it - pids.begin()];
354 process_memory->pid = *it;
355 if (!CollectProcessMemoryInformation(page_count_fd, process_memory))
356 return EXIT_FAILURE;
357 }
358 }
359 ClassifyPages(&processes_memory);
360 DumpProcessesMemoryMaps(processes_memory);
361 return EXIT_SUCCESS;
362 }
OLDNEW
« no previous file with comments | « tools/android/android_tools.gyp ('k') | tools/android/memdump/memdump.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698