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

Side by Side Diff: tools/deep_memory_profiler/lib/sorter.py

Issue 19346002: Refactor dmprof: Split dmprof.py into modules. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 5 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 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 cStringIO
6 import json
7 import logging
8 import os
9 import re
10
11
12 LOGGER = logging.getLogger('dmprof')
13
14 BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
15
16 DEFAULT_SORTERS = [
17 os.path.join(BASE_PATH, 'sorter.malloc-component.json'),
18 os.path.join(BASE_PATH, 'sorter.malloc-type.json'),
19 os.path.join(BASE_PATH, 'sorter.vm-map.json'),
20 os.path.join(BASE_PATH, 'sorter.vm-sharing.json'),
21 ]
22
23
24 class Unit(object):
25 """Represents a minimum unit of memory usage categorization.
26
27 It is supposed to be inherited for some different spaces like the entire
28 virtual memory and malloc arena. Such different spaces are called "worlds"
29 in dmprof. (For example, the "vm" world and the "malloc" world.)
30 """
31 def __init__(self, unit_id, size):
32 self._unit_id = unit_id
33 self._size = size
34
35 @property
36 def unit_id(self):
37 return self._unit_id
38
39 @property
40 def size(self):
41 return self._size
42
43
44 class VMUnit(Unit):
45 """Represents a Unit for a memory region on virtual memory."""
46 def __init__(self, unit_id, committed, reserved, mmap, region,
47 pageframe=None, group_pfn_counts=None):
48 super(VMUnit, self).__init__(unit_id, committed)
49 self._reserved = reserved
50 self._mmap = mmap
51 self._region = region
52 self._pageframe = pageframe
53 self._group_pfn_counts = group_pfn_counts
54
55 @property
56 def committed(self):
57 return self._size
58
59 @property
60 def reserved(self):
61 return self._reserved
62
63 @property
64 def mmap(self):
65 return self._mmap
66
67 @property
68 def region(self):
69 return self._region
70
71 @property
72 def pageframe(self):
73 return self._pageframe
74
75 @property
76 def group_pfn_counts(self):
77 return self._group_pfn_counts
78
79
80 class MMapUnit(VMUnit):
81 """Represents a Unit for a mmap'ed region."""
82 def __init__(self, unit_id, committed, reserved, region, bucket_set,
83 pageframe=None, group_pfn_counts=None):
84 super(MMapUnit, self).__init__(unit_id, committed, reserved, True,
85 region, pageframe, group_pfn_counts)
86 self._bucket_set = bucket_set
87
88 def __repr__(self):
89 return str(self.region)
90
91 @property
92 def bucket_set(self):
93 return self._bucket_set
94
95
96 class UnhookedUnit(VMUnit):
97 """Represents a Unit for a non-mmap'ed memory region on virtual memory."""
98 def __init__(self, unit_id, committed, reserved, region,
99 pageframe=None, group_pfn_counts=None):
100 super(UnhookedUnit, self).__init__(unit_id, committed, reserved, False,
101 region, pageframe, group_pfn_counts)
102
103 def __repr__(self):
104 return str(self.region)
105
106
107 class MallocUnit(Unit):
108 """Represents a Unit for a malloc'ed memory block."""
109 def __init__(self, unit_id, size, alloc_count, free_count, bucket):
110 super(MallocUnit, self).__init__(unit_id, size)
111 self._bucket = bucket
112 self._alloc_count = alloc_count
113 self._free_count = free_count
114
115 def __repr__(self):
116 return str(self.bucket)
117
118 @property
119 def bucket(self):
120 return self._bucket
121
122 @property
123 def alloc_count(self):
124 return self._alloc_count
125
126 @property
127 def free_count(self):
128 return self._free_count
129
130
131 class UnitSet(object):
132 """Represents an iterable set of Units."""
133 def __init__(self, world):
134 self._units = {}
135 self._world = world
136
137 def __repr__(self):
138 return str(self._units)
139
140 def __iter__(self):
141 for unit_id in sorted(self._units):
142 yield self._units[unit_id]
143
144 def append(self, unit, overwrite=False):
145 if not overwrite and unit.unit_id in self._units:
146 LOGGER.error('The unit id=%s already exists.' % str(unit.unit_id))
147 self._units[unit.unit_id] = unit
148
149
150 class AbstractRule(object):
151 """An abstract class for rules to be matched with units."""
152 def __init__(self, dct):
153 self._name = dct['name']
154 self._hidden = dct.get('hidden', False)
155 self._subworlds = dct.get('subworlds', [])
156
157 def match(self, unit):
158 raise NotImplementedError()
159
160 @property
161 def name(self):
162 return self._name
163
164 @property
165 def hidden(self):
166 return self._hidden
167
168 def iter_subworld(self):
169 for subworld in self._subworlds:
170 yield subworld
171
172
173 class VMRule(AbstractRule):
174 """Represents a Rule to match with virtual memory regions."""
175 def __init__(self, dct):
176 super(VMRule, self).__init__(dct)
177 self._backtrace_function = dct.get('backtrace_function', None)
178 if self._backtrace_function:
179 self._backtrace_function = re.compile(self._backtrace_function)
180 self._backtrace_sourcefile = dct.get('backtrace_sourcefile', None)
181 if self._backtrace_sourcefile:
182 self._backtrace_sourcefile = re.compile(self._backtrace_sourcefile)
183 self._mmap = dct.get('mmap', None)
184 self._sharedwith = dct.get('sharedwith', [])
185 self._mapped_pathname = dct.get('mapped_pathname', None)
186 if self._mapped_pathname:
187 self._mapped_pathname = re.compile(self._mapped_pathname)
188 self._mapped_permission = dct.get('mapped_permission', None)
189 if self._mapped_permission:
190 self._mapped_permission = re.compile(self._mapped_permission)
191
192 def __repr__(self):
193 result = cStringIO.StringIO()
194 result.write('{"%s"=>' % self._name)
195 attributes = []
196 attributes.append('mmap: %s' % self._mmap)
197 if self._backtrace_function:
198 attributes.append('backtrace_function: "%s"' %
199 self._backtrace_function.pattern)
200 if self._sharedwith:
201 attributes.append('sharedwith: "%s"' % self._sharedwith)
202 if self._mapped_pathname:
203 attributes.append('mapped_pathname: "%s"' % self._mapped_pathname.pattern)
204 if self._mapped_permission:
205 attributes.append('mapped_permission: "%s"' %
206 self._mapped_permission.pattern)
207 result.write('%s}' % ', '.join(attributes))
208 return result.getvalue()
209
210 def match(self, unit):
211 if unit.mmap:
212 assert unit.region[0] == 'hooked'
213 bucket = unit.bucket_set.get(unit.region[1]['bucket_id'])
214 assert bucket
215 assert bucket.allocator_type == 'mmap'
216
217 stackfunction = bucket.symbolized_joined_stackfunction
218 stacksourcefile = bucket.symbolized_joined_stacksourcefile
219
220 # TODO(dmikurube): Support shared memory.
221 sharedwith = None
222
223 if self._mmap == False: # (self._mmap == None) should go through.
224 return False
225 if (self._backtrace_function and
226 not self._backtrace_function.match(stackfunction)):
227 return False
228 if (self._backtrace_sourcefile and
229 not self._backtrace_sourcefile.match(stacksourcefile)):
230 return False
231 if (self._mapped_pathname and
232 not self._mapped_pathname.match(unit.region[1]['vma']['name'])):
233 return False
234 if (self._mapped_permission and
235 not self._mapped_permission.match(
236 unit.region[1]['vma']['readable'] +
237 unit.region[1]['vma']['writable'] +
238 unit.region[1]['vma']['executable'] +
239 unit.region[1]['vma']['private'])):
240 return False
241 if (self._sharedwith and
242 unit.pageframe and sharedwith not in self._sharedwith):
243 return False
244
245 return True
246
247 else:
248 assert unit.region[0] == 'unhooked'
249
250 # TODO(dmikurube): Support shared memory.
251 sharedwith = None
252
253 if self._mmap == True: # (self._mmap == None) should go through.
254 return False
255 if (self._mapped_pathname and
256 not self._mapped_pathname.match(unit.region[1]['vma']['name'])):
257 return False
258 if (self._mapped_permission and
259 not self._mapped_permission.match(
260 unit.region[1]['vma']['readable'] +
261 unit.region[1]['vma']['writable'] +
262 unit.region[1]['vma']['executable'] +
263 unit.region[1]['vma']['private'])):
264 return False
265 if (self._sharedwith and
266 unit.pageframe and sharedwith not in self._sharedwith):
267 return False
268
269 return True
270
271
272 class MallocRule(AbstractRule):
273 """Represents a Rule to match with malloc'ed blocks."""
274 def __init__(self, dct):
275 super(MallocRule, self).__init__(dct)
276 self._backtrace_function = dct.get('backtrace_function', None)
277 if self._backtrace_function:
278 self._backtrace_function = re.compile(self._backtrace_function)
279 self._backtrace_sourcefile = dct.get('backtrace_sourcefile', None)
280 if self._backtrace_sourcefile:
281 self._backtrace_sourcefile = re.compile(self._backtrace_sourcefile)
282 self._typeinfo = dct.get('typeinfo', None)
283 if self._typeinfo:
284 self._typeinfo = re.compile(self._typeinfo)
285
286 def __repr__(self):
287 result = cStringIO.StringIO()
288 result.write('{"%s"=>' % self._name)
289 attributes = []
290 if self._backtrace_function:
291 attributes.append('backtrace_function: "%s"' % self._backtrace_function)
292 if self._typeinfo:
293 attributes.append('typeinfo: "%s"' % self._typeinfo)
294 result.write('%s}' % ', '.join(attributes))
295 return result.getvalue()
296
297 def match(self, unit):
298 assert unit.bucket.allocator_type == 'malloc'
299
300 stackfunction = unit.bucket.symbolized_joined_stackfunction
301 stacksourcefile = unit.bucket.symbolized_joined_stacksourcefile
302 typeinfo = unit.bucket.symbolized_typeinfo
303 if typeinfo.startswith('0x'):
304 typeinfo = unit.bucket.typeinfo_name
305
306 return ((not self._backtrace_function or
307 self._backtrace_function.match(stackfunction)) and
308 (not self._backtrace_sourcefile or
309 self._backtrace_sourcefile.match(stacksourcefile)) and
310 (not self._typeinfo or self._typeinfo.match(typeinfo)))
311
312
313 class NoBucketMallocRule(MallocRule):
314 """Represents a Rule that small ignorable units match with."""
315 def __init__(self):
316 super(NoBucketMallocRule, self).__init__({'name': 'tc-no-bucket'})
317 self._no_bucket = True
318
319 @property
320 def no_bucket(self):
321 return self._no_bucket
322
323
324 class AbstractSorter(object):
325 """An abstract class for classifying Units with a set of Rules."""
326 def __init__(self, dct):
327 self._type = 'sorter'
328 self._version = dct['version']
329 self._world = dct['world']
330 self._name = dct['name']
331 self._order = dct['order']
332
333 self._rules = []
334 for rule in dct['rules']:
335 if dct['world'] == 'vm':
336 self._rules.append(VMRule(rule))
337 elif dct['world'] == 'malloc':
338 self._rules.append(MallocRule(rule))
339 else:
340 LOGGER.error('Unknown sorter world type')
341
342 def __repr__(self):
343 result = cStringIO.StringIO()
344 result.write('world=%s' % self._world)
345 result.write('order=%s' % self._order)
346 result.write('rules:')
347 for rule in self._rules:
348 result.write(' %s' % rule)
349 return result.getvalue()
350
351 @staticmethod
352 def load(filename):
353 with open(filename) as sorter_f:
354 sorter_dict = json.load(sorter_f)
355 if sorter_dict['world'] == 'vm':
356 return VMSorter(sorter_dict)
357 elif sorter_dict['world'] == 'malloc':
358 return MallocSorter(sorter_dict)
359 else:
360 LOGGER.error('Unknown sorter world type')
361 return None
362
363 @property
364 def world(self):
365 return self._world
366
367 @property
368 def name(self):
369 return self._name
370
371 def find(self, unit):
372 raise NotImplementedError()
373
374 def find_rule(self, name):
375 """Finds a rule whose name is |name|. """
376 for rule in self._rules:
377 if rule.name == name:
378 return rule
379 return None
380
381
382 class VMSorter(AbstractSorter):
383 """Represents a Sorter for memory regions on virtual memory."""
384 def __init__(self, dct):
385 assert dct['world'] == 'vm'
386 super(VMSorter, self).__init__(dct)
387
388 def find(self, unit):
389 for rule in self._rules:
390 if rule.match(unit):
391 return rule
392 assert False
393
394
395 class MallocSorter(AbstractSorter):
396 """Represents a Sorter for malloc'ed blocks."""
397 def __init__(self, dct):
398 assert dct['world'] == 'malloc'
399 super(MallocSorter, self).__init__(dct)
400 self._no_bucket_rule = NoBucketMallocRule()
401
402 def find(self, unit):
403 if not unit.bucket:
404 return self._no_bucket_rule
405 assert unit.bucket.allocator_type == 'malloc'
406
407 if unit.bucket.component_cache:
408 return unit.bucket.component_cache
409
410 for rule in self._rules:
411 if rule.match(unit):
412 unit.bucket.component_cache = rule
413 return rule
414 assert False
415
416
417 class SorterSet(object):
418 """Represents an iterable set of Sorters."""
419 def __init__(self, additional=None, default=None):
420 if not additional:
421 additional = []
422 if not default:
423 default = DEFAULT_SORTERS
424 self._sorters = {}
425 for filename in default + additional:
426 sorter = AbstractSorter.load(filename)
427 if sorter.world not in self._sorters:
428 self._sorters[sorter.world] = []
429 self._sorters[sorter.world].append(sorter)
430
431 def __repr__(self):
432 result = cStringIO.StringIO()
433 result.write(self._sorters)
434 return result.getvalue()
435
436 def __iter__(self):
437 for sorters in self._sorters.itervalues():
438 for sorter in sorters:
439 yield sorter
440
441 def iter_world(self, world):
442 for sorter in self._sorters.get(world, []):
443 yield sorter
OLDNEW
« no previous file with comments | « tools/deep_memory_profiler/lib/range_dict.py ('k') | tools/deep_memory_profiler/lib/subcommand.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698