Index: tools/deep_memory_profiler/lib/pageframe.py |
diff --git a/tools/deep_memory_profiler/lib/pageframe.py b/tools/deep_memory_profiler/lib/pageframe.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..8722243baf611cde0703f550d5b48c9f38138ab9 |
--- /dev/null |
+++ b/tools/deep_memory_profiler/lib/pageframe.py |
@@ -0,0 +1,163 @@ |
+# Copyright 2013 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+import logging |
+import os |
+import re |
+import struct |
+ |
+ |
+LOGGER = logging.getLogger('dmprof') |
+ |
+ |
+class PageFrame(object): |
+ """Represents a pageframe and maybe its shared count.""" |
+ def __init__(self, pfn, size, pagecount, start_truncated, end_truncated): |
+ self._pfn = pfn |
+ self._size = size |
+ self._pagecount = pagecount |
+ self._start_truncated = start_truncated |
+ self._end_truncated = end_truncated |
+ |
+ def __str__(self): |
+ result = str() |
+ if self._start_truncated: |
+ result += '<' |
+ result += '%06x#%d' % (self._pfn, self._pagecount) |
+ if self._end_truncated: |
+ result += '>' |
+ return result |
+ |
+ def __repr__(self): |
+ return str(self) |
+ |
+ @staticmethod |
+ def parse(encoded_pfn, size): |
+ start = 0 |
+ end = len(encoded_pfn) |
+ end_truncated = False |
+ if encoded_pfn.endswith('>'): |
+ end = len(encoded_pfn) - 1 |
+ end_truncated = True |
+ pagecount_found = encoded_pfn.find('#') |
+ pagecount = None |
+ if pagecount_found >= 0: |
+ encoded_pagecount = 'AAA' + encoded_pfn[pagecount_found+1 : end] |
+ pagecount = struct.unpack( |
+ '>I', '\x00' + encoded_pagecount.decode('base64'))[0] |
+ end = pagecount_found |
+ start_truncated = False |
+ if encoded_pfn.startswith('<'): |
+ start = 1 |
+ start_truncated = True |
+ |
+ pfn = struct.unpack( |
+ '>I', '\x00' + (encoded_pfn[start:end]).decode('base64'))[0] |
+ |
+ return PageFrame(pfn, size, pagecount, start_truncated, end_truncated) |
+ |
+ @property |
+ def pfn(self): |
+ return self._pfn |
+ |
+ @property |
+ def size(self): |
+ return self._size |
+ |
+ def set_size(self, size): |
+ self._size = size |
+ |
+ @property |
+ def pagecount(self): |
+ return self._pagecount |
+ |
+ @property |
+ def start_truncated(self): |
+ return self._start_truncated |
+ |
+ @property |
+ def end_truncated(self): |
+ return self._end_truncated |
+ |
+ |
+class PFNCounts(object): |
+ """Represents counts of PFNs in a process.""" |
+ |
+ _PATH_PATTERN = re.compile(r'^(.*)\.([0-9]+)\.([0-9]+)\.heap$') |
+ |
+ def __init__(self, path, modified_time): |
+ matched = self._PATH_PATTERN.match(path) |
+ if matched: |
+ self._pid = int(matched.group(2)) |
+ else: |
+ self._pid = 0 |
+ self._command_line = '' |
+ self._pagesize = 4096 |
+ self._path = path |
+ self._pfn_meta = '' |
+ self._pfnset = {} |
+ self._reason = '' |
+ self._time = modified_time |
+ |
+ @staticmethod |
+ def load(path, log_header='Loading PFNs from a heap profile dump: '): |
+ pfnset = PFNCounts(path, float(os.stat(path).st_mtime)) |
+ LOGGER.info('%s%s' % (log_header, path)) |
+ |
+ with open(path, 'r') as pfnset_f: |
+ pfnset.load_file(pfnset_f) |
+ |
+ return pfnset |
+ |
+ @property |
+ def path(self): |
+ return self._path |
+ |
+ @property |
+ def pid(self): |
+ return self._pid |
+ |
+ @property |
+ def time(self): |
+ return self._time |
+ |
+ @property |
+ def reason(self): |
+ return self._reason |
+ |
+ @property |
+ def iter_pfn(self): |
+ for pfn, count in self._pfnset.iteritems(): |
+ yield pfn, count |
+ |
+ def load_file(self, pfnset_f): |
+ prev_pfn_end_truncated = None |
+ for line in pfnset_f: |
+ line = line.strip() |
+ if line.startswith('GLOBAL_STATS:') or line.startswith('STACKTRACES:'): |
+ break |
+ elif line.startswith('PF: '): |
+ for encoded_pfn in line[3:].split(): |
+ page_frame = PageFrame.parse(encoded_pfn, self._pagesize) |
+ if page_frame.start_truncated and ( |
+ not prev_pfn_end_truncated or |
+ prev_pfn_end_truncated != page_frame.pfn): |
+ LOGGER.error('Broken page frame number: %s.' % encoded_pfn) |
+ self._pfnset[page_frame.pfn] = self._pfnset.get(page_frame.pfn, 0) + 1 |
+ if page_frame.end_truncated: |
+ prev_pfn_end_truncated = page_frame.pfn |
+ else: |
+ prev_pfn_end_truncated = None |
+ elif line.startswith('PageSize: '): |
+ self._pagesize = int(line[10:]) |
+ elif line.startswith('PFN: '): |
+ self._pfn_meta = line[5:] |
+ elif line.startswith('PageFrame: '): |
+ self._pfn_meta = line[11:] |
+ elif line.startswith('Time: '): |
+ self._time = float(line[6:]) |
+ elif line.startswith('CommandLine: '): |
+ self._command_line = line[13:] |
+ elif line.startswith('Reason: '): |
+ self._reason = line[8:] |