OLD | NEW |
(Empty) | |
| 1 # Copyright 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 import logging |
| 6 import os |
| 7 import re |
| 8 import struct |
| 9 |
| 10 |
| 11 LOGGER = logging.getLogger('dmprof') |
| 12 |
| 13 |
| 14 class PageFrame(object): |
| 15 """Represents a pageframe and maybe its shared count.""" |
| 16 def __init__(self, pfn, size, pagecount, start_truncated, end_truncated): |
| 17 self._pfn = pfn |
| 18 self._size = size |
| 19 self._pagecount = pagecount |
| 20 self._start_truncated = start_truncated |
| 21 self._end_truncated = end_truncated |
| 22 |
| 23 def __str__(self): |
| 24 result = str() |
| 25 if self._start_truncated: |
| 26 result += '<' |
| 27 result += '%06x#%d' % (self._pfn, self._pagecount) |
| 28 if self._end_truncated: |
| 29 result += '>' |
| 30 return result |
| 31 |
| 32 def __repr__(self): |
| 33 return str(self) |
| 34 |
| 35 @staticmethod |
| 36 def parse(encoded_pfn, size): |
| 37 start = 0 |
| 38 end = len(encoded_pfn) |
| 39 end_truncated = False |
| 40 if encoded_pfn.endswith('>'): |
| 41 end = len(encoded_pfn) - 1 |
| 42 end_truncated = True |
| 43 pagecount_found = encoded_pfn.find('#') |
| 44 pagecount = None |
| 45 if pagecount_found >= 0: |
| 46 encoded_pagecount = 'AAA' + encoded_pfn[pagecount_found+1 : end] |
| 47 pagecount = struct.unpack( |
| 48 '>I', '\x00' + encoded_pagecount.decode('base64'))[0] |
| 49 end = pagecount_found |
| 50 start_truncated = False |
| 51 if encoded_pfn.startswith('<'): |
| 52 start = 1 |
| 53 start_truncated = True |
| 54 |
| 55 pfn = struct.unpack( |
| 56 '>I', '\x00' + (encoded_pfn[start:end]).decode('base64'))[0] |
| 57 |
| 58 return PageFrame(pfn, size, pagecount, start_truncated, end_truncated) |
| 59 |
| 60 @property |
| 61 def pfn(self): |
| 62 return self._pfn |
| 63 |
| 64 @property |
| 65 def size(self): |
| 66 return self._size |
| 67 |
| 68 def set_size(self, size): |
| 69 self._size = size |
| 70 |
| 71 @property |
| 72 def pagecount(self): |
| 73 return self._pagecount |
| 74 |
| 75 @property |
| 76 def start_truncated(self): |
| 77 return self._start_truncated |
| 78 |
| 79 @property |
| 80 def end_truncated(self): |
| 81 return self._end_truncated |
| 82 |
| 83 |
| 84 class PFNCounts(object): |
| 85 """Represents counts of PFNs in a process.""" |
| 86 |
| 87 _PATH_PATTERN = re.compile(r'^(.*)\.([0-9]+)\.([0-9]+)\.heap$') |
| 88 |
| 89 def __init__(self, path, modified_time): |
| 90 matched = self._PATH_PATTERN.match(path) |
| 91 if matched: |
| 92 self._pid = int(matched.group(2)) |
| 93 else: |
| 94 self._pid = 0 |
| 95 self._command_line = '' |
| 96 self._pagesize = 4096 |
| 97 self._path = path |
| 98 self._pfn_meta = '' |
| 99 self._pfnset = {} |
| 100 self._reason = '' |
| 101 self._time = modified_time |
| 102 |
| 103 @staticmethod |
| 104 def load(path, log_header='Loading PFNs from a heap profile dump: '): |
| 105 pfnset = PFNCounts(path, float(os.stat(path).st_mtime)) |
| 106 LOGGER.info('%s%s' % (log_header, path)) |
| 107 |
| 108 with open(path, 'r') as pfnset_f: |
| 109 pfnset.load_file(pfnset_f) |
| 110 |
| 111 return pfnset |
| 112 |
| 113 @property |
| 114 def path(self): |
| 115 return self._path |
| 116 |
| 117 @property |
| 118 def pid(self): |
| 119 return self._pid |
| 120 |
| 121 @property |
| 122 def time(self): |
| 123 return self._time |
| 124 |
| 125 @property |
| 126 def reason(self): |
| 127 return self._reason |
| 128 |
| 129 @property |
| 130 def iter_pfn(self): |
| 131 for pfn, count in self._pfnset.iteritems(): |
| 132 yield pfn, count |
| 133 |
| 134 def load_file(self, pfnset_f): |
| 135 prev_pfn_end_truncated = None |
| 136 for line in pfnset_f: |
| 137 line = line.strip() |
| 138 if line.startswith('GLOBAL_STATS:') or line.startswith('STACKTRACES:'): |
| 139 break |
| 140 elif line.startswith('PF: '): |
| 141 for encoded_pfn in line[3:].split(): |
| 142 page_frame = PageFrame.parse(encoded_pfn, self._pagesize) |
| 143 if page_frame.start_truncated and ( |
| 144 not prev_pfn_end_truncated or |
| 145 prev_pfn_end_truncated != page_frame.pfn): |
| 146 LOGGER.error('Broken page frame number: %s.' % encoded_pfn) |
| 147 self._pfnset[page_frame.pfn] = self._pfnset.get(page_frame.pfn, 0) + 1 |
| 148 if page_frame.end_truncated: |
| 149 prev_pfn_end_truncated = page_frame.pfn |
| 150 else: |
| 151 prev_pfn_end_truncated = None |
| 152 elif line.startswith('PageSize: '): |
| 153 self._pagesize = int(line[10:]) |
| 154 elif line.startswith('PFN: '): |
| 155 self._pfn_meta = line[5:] |
| 156 elif line.startswith('PageFrame: '): |
| 157 self._pfn_meta = line[11:] |
| 158 elif line.startswith('Time: '): |
| 159 self._time = float(line[6:]) |
| 160 elif line.startswith('CommandLine: '): |
| 161 self._command_line = line[13:] |
| 162 elif line.startswith('Reason: '): |
| 163 self._reason = line[8:] |
OLD | NEW |