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.
11 LOGGER
= logging
.getLogger('dmprof')
14 class PageFrame(object):
15 """Represents a pageframe and maybe its shared count."""
16 def __init__(self
, pfn
, size
, pagecount
, start_truncated
, end_truncated
):
19 self
._pagecount
= pagecount
20 self
._start
_truncated
= start_truncated
21 self
._end
_truncated
= end_truncated
25 if self
._start
_truncated
:
27 result
+= '%06x#%d' % (self
._pfn
, self
._pagecount
)
28 if self
._end
_truncated
:
36 def parse(encoded_pfn
, size
):
38 end
= len(encoded_pfn
)
40 if encoded_pfn
.endswith('>'):
41 end
= len(encoded_pfn
) - 1
43 pagecount_found
= encoded_pfn
.find('#')
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]
50 start_truncated
= False
51 if encoded_pfn
.startswith('<'):
53 start_truncated
= True
56 '>I', '\x00' + (encoded_pfn
[start
:end
]).decode('base64'))[0]
58 return PageFrame(pfn
, size
, pagecount
, start_truncated
, end_truncated
)
68 def set_size(self
, size
):
73 return self
._pagecount
76 def start_truncated(self
):
77 return self
._start
_truncated
80 def end_truncated(self
):
81 return self
._end
_truncated
84 class PFNCounts(object):
85 """Represents counts of PFNs in a process."""
87 _PATH_PATTERN
= re
.compile(r
'^(.*)\.([0-9]+)\.([0-9]+)\.heap$')
89 def __init__(self
, path
, modified_time
):
90 matched
= self
._PATH
_PATTERN
.match(path
)
92 self
._pid
= int(matched
.group(2))
95 self
._command
_line
= ''
101 self
._time
= modified_time
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
))
108 with
open(path
, 'r') as pfnset_f
:
109 pfnset
.load_file(pfnset_f
)
131 for pfn
, count
in self
._pfnset
.iteritems():
134 def load_file(self
, pfnset_f
):
135 prev_pfn_end_truncated
= None
136 for line
in pfnset_f
:
138 if line
.startswith('GLOBAL_STATS:') or line
.startswith('STACKTRACES:'):
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
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:]