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

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: Polish 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
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<int64, 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 static const char kExample[] = "7fe94ae7d000-7fe94bbd0000 r--p";
bulach 2013/05/30 11:47:07 nit: instead of strlen below, perhaps: static cons
Philippe 2013/05/30 13:48:43 Done. I got rid of this and addressed when I addre
70 do {
71 tokens->clear();
72 if (line.length() < strlen(kExample))
73 break;
74 base::SplitString(line, ' ', tokens);
75 if (tokens->size() < 2)
76 break;
77 const std::string& addr_range = tokens->at(0);
78 int64 tmp;
79 if (!base::HexStringToInt64(
80 base::StringPiece(addr_range.begin(), addr_range.begin() + 8),
81 &tmp)) {
82 break;
83 }
84 memory_map->start_address = static_cast<uint>(tmp);
85 if (!base::HexStringToInt64(
86 base::StringPiece(addr_range.begin() + 9,
87 addr_range.begin() + 9 + 8), &tmp)) {
88 break;
89 }
90 memory_map->end_address = static_cast<uint>(tmp);
91 const int kModeLength = strlen("rwxp");
92 if (tokens->at(1).size() < kModeLength)
93 break;
94 memory_map->flags.swap(tokens->at(1));
95 if (tokens->size() >= 6) {
96 for (std::vector<std::string>::const_iterator it = tokens->begin() + 5;
97 it != tokens->end(); ++it) {
98 if (!it->empty()) {
99 if (!memory_map->name.empty())
100 memory_map->name.append(" ");
101 memory_map->name.append(*it);
102 }
103 }
104 }
105 return true;
106 } while (false);
bulach 2013/05/30 11:47:07 nit: perhaps split into a TokenizeMemoryMapLine()
Philippe 2013/05/30 13:48:43 Good idea. I just moved the error message to the c
107
108 LOG(ERROR) << "Could not parse line: " << line;
109 return false;
110 }
111
112 // Reads sizeof(*value) bytes from file |fd| at |offset|.
bulach 2013/05/30 11:47:07 sizeof(T), and in 119 as well?
Philippe 2013/05/30 13:48:43 I made the change here but kept line 119 unchanged
113 template <typename T>
114 bool ReadFromFileAtOffset(int fd, off_t offset, T* value) {
qsr 2013/05/30 10:30:25 You might want to use mmap instead of opening your
digit 2013/05/30 10:34:09 From past experience, most files in /proc cannot b
115 if (lseek64(fd, offset * sizeof(*value), SEEK_SET) < 0) {
116 PLOG(ERROR) << "lseek";
117 return false;
118 }
119 ssize_t bytes = read(fd, value, sizeof(*value));
120 if (bytes < 0) {
qsr 2013/05/30 10:30:25 You could replace 0 by sizeof(*value) if you do no
Philippe 2013/05/30 13:48:43 Done.
121 PLOG(ERROR) << "read";
122 return false;
123 }
124 return true;
125 }
126
127 // Fills |process_maps| in with the process memory maps identified by |pid|.
128 bool GetProcessMaps(pid_t pid, std::vector<MemoryMap>* process_maps) {
129 std::ifstream maps_file(base::StringPrintf("/proc/%d/maps", pid).c_str());
130 if (!maps_file.good()) {
131 PLOG(ERROR) << "open";
132 return false;
133 }
134 std::string line;
135 std::vector<std::string> tokens;
136 while (std::getline(maps_file, line) && !line.empty()) {
137 MemoryMap memory_map = {};
138 if (!ParseMemoryMapLine(line, &tokens, &memory_map))
139 return false;
140 process_maps->push_back(memory_map);
141 }
142 return true;
143 }
144
145 // Fills |committed_pages| in with the set of committed pages contained in the
146 // provided memory map.
147 bool GetPagesForMemoryMap(int pagemap_fd,
148 const MemoryMap& memory_map,
149 std::vector<PageInfo>* committed_pages) {
150 for (uint addr = memory_map.start_address; addr < memory_map.end_address;
151 addr += PAGE_SIZE) {
152 DCHECK_EQ(0, addr % PAGE_SIZE);
153 PageMapEntry page_map_entry = {};
154 COMPILE_ASSERT(sizeof(PageMapEntry) == sizeof(uint64), unexpected_size);
155 const off64_t offset = addr / PAGE_SIZE;
156 if (!ReadFromFileAtOffset(pagemap_fd, offset, &page_map_entry))
157 return false;
158 if (page_map_entry.present) { // Ignore non-committed pages.
159 if (page_map_entry.page_frame_number == 0)
160 continue;
161 PageInfo page_info = {};
162 page_info.page_frame_number = page_map_entry.page_frame_number;
163 committed_pages->push_back(page_info);
164 }
165 }
166 return true;
167 }
168
169 bool SetTimesMapped(int pagecount_fd, std::vector<PageInfo>* pages) {
170 for (std::vector<PageInfo>::iterator it = pages->begin();
171 it != pages->end(); ++it) {
172 PageInfo* const page_info = &*it;
173 int64 times_mapped;
174 if (!ReadFromFileAtOffset(
175 pagecount_fd, page_info->page_frame_number, &times_mapped)) {
176 return false;
177 }
178 page_info->times_mapped = times_mapped;
179 }
180 return true;
181 }
182
183 // Fills in the provided vector of Page Frame Number maps. This lets
184 // ClassifyPages() know how many times each page is mapped in the processes.
185 void FillPFNMaps(const std::vector<ProcessMemory>& processes_memory,
186 std::vector<PFNMap>* pfn_maps) {
187 int current_process_index = 0;
188 for (std::vector<ProcessMemory>::const_iterator it = processes_memory.begin();
189 it != processes_memory.end(); ++it, ++current_process_index) {
190 const std::vector<MemoryMap>& memory_maps = it->memory_maps;
191 for (std::vector<MemoryMap>::const_iterator it = memory_maps.begin();
192 it != memory_maps.end(); ++it) {
193 const std::vector<PageInfo>& pages = it->committed_pages;
194 for (std::vector<PageInfo>::const_iterator it = pages.begin();
195 it != pages.end(); ++it) {
196 const PageInfo& page_info = *it;
197 PFNMap* const pfn_map = &(*pfn_maps)[current_process_index];
198 const std::pair<PFNMap::iterator, bool> result = pfn_map->insert(
199 std::make_pair(page_info.page_frame_number, 0));
200 ++result.first->second;
201 }
202 }
203 }
204 }
205
206 // Sets the private_count/app_shared_count/other_shared_count fields of the
207 // provided memory maps for each process.
208 void ClassifyPages(std::vector<ProcessMemory>* processes_memory) {
209 std::vector<PFNMap> pfn_maps(processes_memory->size());
210 FillPFNMaps(*processes_memory, &pfn_maps);
211 for (std::vector<ProcessMemory>::iterator it = processes_memory->begin();
212 it != processes_memory->end(); ++it) {
213 std::vector<MemoryMap>* const memory_maps = &it->memory_maps;
214 for (std::vector<MemoryMap>::iterator it = memory_maps->begin();
215 it != memory_maps->end(); ++it) {
216 MemoryMap* const memory_map = &*it;
217 const std::vector<PageInfo>& pages = memory_map->committed_pages;
218 for (std::vector<PageInfo>::const_iterator it = pages.begin();
219 it != pages.end(); ++it) {
220 const PageInfo& page_info = *it;
221 if (page_info.times_mapped == 1) {
222 ++memory_map->private_count;
223 continue;
224 }
225 bool mapped_in_multiple_processes = false;
226 int times_mapped = 0;
227 // See if the current physical page is also mapped in the processes that
228 // are being analyzed.
229 for (std::vector<PFNMap>::const_iterator it = pfn_maps.begin();
230 it != pfn_maps.end(); ++it) {
231 const PFNMap& pfn_map = *it;
232 const PFNMap::const_iterator found_it = pfn_map.find(
233 page_info.page_frame_number);
234 if (found_it == pfn_map.end())
235 continue;
236 if (times_mapped)
237 mapped_in_multiple_processes = true;
238 times_mapped += found_it->second;
239 }
240 if (times_mapped == page_info.times_mapped) {
241 // The physical page is only mapped in the processes that are being
242 // analyzed.
243 if (mapped_in_multiple_processes)
244 ++memory_map->app_shared_count;
245 else // Mapped multiple times in the same process.
246 ++memory_map->private_count;
247 } else {
248 ++memory_map->other_shared_count;
249 }
250 }
251 }
252 }
253 }
254
255 void DumpProcessesMemoryMaps(
256 const std::vector<ProcessMemory>& processes_memory) {
257 std::string buf;
258 for (std::vector<ProcessMemory>::const_iterator it = processes_memory.begin();
259 it != processes_memory.end(); ++it) {
260 const ProcessMemory& process_memory = *it;
261 std::cout << "[ PID=" << process_memory.pid << "]" << '\n';
262 const std::vector<MemoryMap>& memory_maps = process_memory.memory_maps;
263 for (std::vector<MemoryMap>::const_iterator it = memory_maps.begin();
264 it != memory_maps.end(); ++it) {
265 const MemoryMap& memory_map = *it;
266 base::SStringPrintf(
267 &buf, "%x-%x %s %d %d %d %s\n", memory_map.start_address,
268 memory_map.end_address, memory_map.flags.c_str(),
269 memory_map.private_count * PAGE_SIZE,
270 memory_map.app_shared_count * PAGE_SIZE,
271 memory_map.other_shared_count * PAGE_SIZE,
272 memory_map.name.c_str());
273 std::cout << buf;
274 }
275 }
276 }
277
278 void ResumeProcesses(const std::vector<pid_t>& pids) {
279 for (std::vector<pid_t>::const_iterator it = pids.begin(); it != pids.end();
280 ++it) {
281 kill(*it, SIGCONT);
282 }
283 }
284
285 } // namespace
286
287 int main(int argc, char** argv) {
288 if (argc == 1) {
289 LOG(ERROR) << "Usage: " << argv[0] << " <PID1>... <PIDN>";
290 return EXIT_FAILURE;
291 }
292 std::vector<pid_t> pids;
293 for (const char* const* ptr = argv + 1; *ptr; ++ptr) {
294 pid_t pid;
295 if (!base::StringToInt(*ptr, &pid))
296 return EXIT_FAILURE;
297 pids.push_back(pid);
298 }
299 int page_count_fd = open("/proc/kpagecount", O_RDONLY);
300 if (page_count_fd < 0) {
301 PLOG(ERROR) << "open";
302 return EXIT_FAILURE;
303 }
304 file_util::ScopedFD page_count_fd_closer(&page_count_fd);
305
306 base::ScopedClosureRunner auto_resume_processes(
307 base::Bind(&ResumeProcesses, pids));
bulach 2013/05/30 11:47:07 s/ResumeProcess/KillAll/, pass the signal in, and
Philippe 2013/05/30 13:48:43 Great idea! I should have thought about it.
308 std::vector<ProcessMemory> processes_memory(pids.size());
309 for (std::vector<pid_t>::const_iterator it = pids.begin(); it != pids.end();
310 ++it) {
311 kill(*it, SIGSTOP);
312 }
313 for (std::vector<pid_t>::const_iterator it = pids.begin(); it != pids.end();
314 ++it) {
315 const pid_t pid = *it;
qsr 2013/05/30 10:30:26 You could extract this content in another method.
Philippe 2013/05/30 13:48:43 Done.
316 int pagemap_fd = open(
317 base::StringPrintf("/proc/%d/pagemap", pid).c_str(), O_RDONLY);
318 if (pagemap_fd < 0) {
319 PLOG(ERROR) << "open";
320 return EXIT_FAILURE;
321 }
322 file_util::ScopedFD auto_closer(&pagemap_fd);
323 ProcessMemory* const process_memory = &processes_memory[it - pids.begin()];
324 process_memory->pid = pid;
325 std::vector<MemoryMap>* const process_maps = &process_memory->memory_maps;
326 if (!GetProcessMaps(pid, process_maps))
327 return EXIT_FAILURE;
328 for (std::vector<MemoryMap>::iterator it = process_maps->begin();
329 it != process_maps->end(); ++it) {
330 std::vector<PageInfo>* const committed_pages =
331 &it->committed_pages;
332 GetPagesForMemoryMap(pagemap_fd, *it, committed_pages);
333 SetTimesMapped(page_count_fd, committed_pages);
334 }
335 }
336 ResumeProcesses(pids);
337 auto_resume_processes.Release();
qsr 2013/05/30 10:30:26 instead of this, you could have it in a scope, and
bulach 2013/05/30 11:47:07 or parhaps: delete 332, and here: auto_resume_proc
Philippe 2013/05/30 13:48:43 I followed Benjamin's suggestion. To me it looks l
338 ClassifyPages(&processes_memory);
339 DumpProcessesMemoryMaps(processes_memory);
340 return EXIT_SUCCESS;
341 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698