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

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: Fix typo 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<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 multiple times in a
213 // single process so that they can be counted as private only once.
214 std::hash_set<uint64> mapped_multiple_times_in_single_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 mapped_multiple_times_in_single_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 bool mapped_in_multiple_processes = false;
232 int times_mapped = 0;
233 const uint64 page_frame_number = page_info.page_frame_number;
234 // See if the current physical page is also mapped in the processes that
235 // are being analyzed.
236 for (std::vector<PFNMap>::const_iterator it = pfn_maps.begin();
237 it != pfn_maps.end(); ++it) {
238 const PFNMap& pfn_map = *it;
239 const PFNMap::const_iterator found_it = pfn_map.find(
240 page_frame_number);
241 if (found_it == pfn_map.end())
242 continue;
243 if (times_mapped)
244 mapped_in_multiple_processes = true;
245 times_mapped += found_it->second;
246 }
247 if (times_mapped == page_info.times_mapped) {
248 // The physical page is only mapped in the processes that are being
249 // analyzed.
250 if (mapped_in_multiple_processes) {
251 ++memory_map->app_shared_count;
252 } else {
253 // The physical page is mapped multiple times in the same process.
254 const std::pair<std::hash_set<uint64>::iterator, bool> result =
255 mapped_multiple_times_in_single_process.insert(
256 page_frame_number);
257 const bool did_insert = result.second;
258 if (did_insert) {
259 // Only count the page once.
260 ++memory_map->private_count;
261 }
262 }
263 } else {
264 ++memory_map->other_shared_count;
265 }
266 }
267 }
268 }
269 }
270
271 void DumpProcessesMemoryMaps(
272 const std::vector<ProcessMemory>& processes_memory) {
273 std::string buf;
274 for (std::vector<ProcessMemory>::const_iterator it = processes_memory.begin();
275 it != processes_memory.end(); ++it) {
276 const ProcessMemory& process_memory = *it;
277 std::cout << "[ PID=" << process_memory.pid << "]" << '\n';
278 const std::vector<MemoryMap>& memory_maps = process_memory.memory_maps;
279 for (std::vector<MemoryMap>::const_iterator it = memory_maps.begin();
280 it != memory_maps.end(); ++it) {
281 const MemoryMap& memory_map = *it;
282 base::SStringPrintf(
283 &buf, "%x-%x %s private=%d shared_app=%d shared_other=%d %s\n",
284 memory_map.start_address,
285 memory_map.end_address, memory_map.flags.c_str(),
286 memory_map.private_count * PAGE_SIZE,
287 memory_map.app_shared_count * PAGE_SIZE,
288 memory_map.other_shared_count * PAGE_SIZE,
289 memory_map.name.c_str());
290 std::cout << buf;
291 }
292 }
293 }
294
295 bool CollectProcessMemoryInformation(int page_count_fd,
296 ProcessMemory* process_memory) {
297 const pid_t pid = process_memory->pid;
298 int pagemap_fd = open(
299 base::StringPrintf("/proc/%d/pagemap", pid).c_str(), O_RDONLY);
300 if (pagemap_fd < 0) {
301 PLOG(ERROR) << "open";
302 return false;
303 }
304 file_util::ScopedFD auto_closer(&pagemap_fd);
305 std::vector<MemoryMap>* const process_maps = &process_memory->memory_maps;
306 if (!GetProcessMaps(pid, process_maps))
307 return false;
308 for (std::vector<MemoryMap>::iterator it = process_maps->begin();
309 it != process_maps->end(); ++it) {
310 std::vector<PageInfo>* const committed_pages = &it->committed_pages;
311 GetPagesForMemoryMap(pagemap_fd, *it, committed_pages);
312 SetTimesMapped(page_count_fd, committed_pages);
313 }
314 return true;
315 }
316
317 void KillAll(const std::vector<pid_t>& pids, int signal_number) {
318 for (std::vector<pid_t>::const_iterator it = pids.begin(); it != pids.end();
319 ++it) {
320 kill(*it, signal_number);
321 }
322 }
323
324 } // namespace
325
326 int main(int argc, char** argv) {
327 if (argc == 1) {
328 LOG(ERROR) << "Usage: " << argv[0] << " <PID1>... <PIDN>";
329 return EXIT_FAILURE;
330 }
331 std::vector<pid_t> pids;
332 for (const char* const* ptr = argv + 1; *ptr; ++ptr) {
333 pid_t pid;
334 if (!base::StringToInt(*ptr, &pid))
335 return EXIT_FAILURE;
336 pids.push_back(pid);
337 }
338 std::vector<ProcessMemory> processes_memory(pids.size());
339 {
340 int page_count_fd = open("/proc/kpagecount", O_RDONLY);
341 if (page_count_fd < 0) {
342 PLOG(ERROR) << "open";
343 return EXIT_FAILURE;
344 }
345 file_util::ScopedFD page_count_fd_closer(&page_count_fd);
346 base::ScopedClosureRunner auto_resume_processes(
347 base::Bind(&KillAll, pids, SIGCONT));
348 KillAll(pids, SIGSTOP);
349 for (std::vector<pid_t>::const_iterator it = pids.begin(); it != pids.end();
350 ++it) {
351 ProcessMemory* const process_memory =
352 &processes_memory[it - pids.begin()];
353 process_memory->pid = *it;
354 if (!CollectProcessMemoryInformation(page_count_fd, process_memory))
355 return EXIT_FAILURE;
356 }
357 }
358 ClassifyPages(&processes_memory);
359 DumpProcessesMemoryMaps(processes_memory);
360 return EXIT_SUCCESS;
361 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698