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

Unified 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 side-by-side diff with in-line comments
Download patch
« 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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/deep_memory_profiler/lib/sorter.py
diff --git a/tools/deep_memory_profiler/lib/sorter.py b/tools/deep_memory_profiler/lib/sorter.py
new file mode 100644
index 0000000000000000000000000000000000000000..db50c70ad71499ffa610439d50da4f9a6c6a97fc
--- /dev/null
+++ b/tools/deep_memory_profiler/lib/sorter.py
@@ -0,0 +1,443 @@
+# 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 cStringIO
+import json
+import logging
+import os
+import re
+
+
+LOGGER = logging.getLogger('dmprof')
+
+BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+DEFAULT_SORTERS = [
+ os.path.join(BASE_PATH, 'sorter.malloc-component.json'),
+ os.path.join(BASE_PATH, 'sorter.malloc-type.json'),
+ os.path.join(BASE_PATH, 'sorter.vm-map.json'),
+ os.path.join(BASE_PATH, 'sorter.vm-sharing.json'),
+ ]
+
+
+class Unit(object):
+ """Represents a minimum unit of memory usage categorization.
+
+ It is supposed to be inherited for some different spaces like the entire
+ virtual memory and malloc arena. Such different spaces are called "worlds"
+ in dmprof. (For example, the "vm" world and the "malloc" world.)
+ """
+ def __init__(self, unit_id, size):
+ self._unit_id = unit_id
+ self._size = size
+
+ @property
+ def unit_id(self):
+ return self._unit_id
+
+ @property
+ def size(self):
+ return self._size
+
+
+class VMUnit(Unit):
+ """Represents a Unit for a memory region on virtual memory."""
+ def __init__(self, unit_id, committed, reserved, mmap, region,
+ pageframe=None, group_pfn_counts=None):
+ super(VMUnit, self).__init__(unit_id, committed)
+ self._reserved = reserved
+ self._mmap = mmap
+ self._region = region
+ self._pageframe = pageframe
+ self._group_pfn_counts = group_pfn_counts
+
+ @property
+ def committed(self):
+ return self._size
+
+ @property
+ def reserved(self):
+ return self._reserved
+
+ @property
+ def mmap(self):
+ return self._mmap
+
+ @property
+ def region(self):
+ return self._region
+
+ @property
+ def pageframe(self):
+ return self._pageframe
+
+ @property
+ def group_pfn_counts(self):
+ return self._group_pfn_counts
+
+
+class MMapUnit(VMUnit):
+ """Represents a Unit for a mmap'ed region."""
+ def __init__(self, unit_id, committed, reserved, region, bucket_set,
+ pageframe=None, group_pfn_counts=None):
+ super(MMapUnit, self).__init__(unit_id, committed, reserved, True,
+ region, pageframe, group_pfn_counts)
+ self._bucket_set = bucket_set
+
+ def __repr__(self):
+ return str(self.region)
+
+ @property
+ def bucket_set(self):
+ return self._bucket_set
+
+
+class UnhookedUnit(VMUnit):
+ """Represents a Unit for a non-mmap'ed memory region on virtual memory."""
+ def __init__(self, unit_id, committed, reserved, region,
+ pageframe=None, group_pfn_counts=None):
+ super(UnhookedUnit, self).__init__(unit_id, committed, reserved, False,
+ region, pageframe, group_pfn_counts)
+
+ def __repr__(self):
+ return str(self.region)
+
+
+class MallocUnit(Unit):
+ """Represents a Unit for a malloc'ed memory block."""
+ def __init__(self, unit_id, size, alloc_count, free_count, bucket):
+ super(MallocUnit, self).__init__(unit_id, size)
+ self._bucket = bucket
+ self._alloc_count = alloc_count
+ self._free_count = free_count
+
+ def __repr__(self):
+ return str(self.bucket)
+
+ @property
+ def bucket(self):
+ return self._bucket
+
+ @property
+ def alloc_count(self):
+ return self._alloc_count
+
+ @property
+ def free_count(self):
+ return self._free_count
+
+
+class UnitSet(object):
+ """Represents an iterable set of Units."""
+ def __init__(self, world):
+ self._units = {}
+ self._world = world
+
+ def __repr__(self):
+ return str(self._units)
+
+ def __iter__(self):
+ for unit_id in sorted(self._units):
+ yield self._units[unit_id]
+
+ def append(self, unit, overwrite=False):
+ if not overwrite and unit.unit_id in self._units:
+ LOGGER.error('The unit id=%s already exists.' % str(unit.unit_id))
+ self._units[unit.unit_id] = unit
+
+
+class AbstractRule(object):
+ """An abstract class for rules to be matched with units."""
+ def __init__(self, dct):
+ self._name = dct['name']
+ self._hidden = dct.get('hidden', False)
+ self._subworlds = dct.get('subworlds', [])
+
+ def match(self, unit):
+ raise NotImplementedError()
+
+ @property
+ def name(self):
+ return self._name
+
+ @property
+ def hidden(self):
+ return self._hidden
+
+ def iter_subworld(self):
+ for subworld in self._subworlds:
+ yield subworld
+
+
+class VMRule(AbstractRule):
+ """Represents a Rule to match with virtual memory regions."""
+ def __init__(self, dct):
+ super(VMRule, self).__init__(dct)
+ self._backtrace_function = dct.get('backtrace_function', None)
+ if self._backtrace_function:
+ self._backtrace_function = re.compile(self._backtrace_function)
+ self._backtrace_sourcefile = dct.get('backtrace_sourcefile', None)
+ if self._backtrace_sourcefile:
+ self._backtrace_sourcefile = re.compile(self._backtrace_sourcefile)
+ self._mmap = dct.get('mmap', None)
+ self._sharedwith = dct.get('sharedwith', [])
+ self._mapped_pathname = dct.get('mapped_pathname', None)
+ if self._mapped_pathname:
+ self._mapped_pathname = re.compile(self._mapped_pathname)
+ self._mapped_permission = dct.get('mapped_permission', None)
+ if self._mapped_permission:
+ self._mapped_permission = re.compile(self._mapped_permission)
+
+ def __repr__(self):
+ result = cStringIO.StringIO()
+ result.write('{"%s"=>' % self._name)
+ attributes = []
+ attributes.append('mmap: %s' % self._mmap)
+ if self._backtrace_function:
+ attributes.append('backtrace_function: "%s"' %
+ self._backtrace_function.pattern)
+ if self._sharedwith:
+ attributes.append('sharedwith: "%s"' % self._sharedwith)
+ if self._mapped_pathname:
+ attributes.append('mapped_pathname: "%s"' % self._mapped_pathname.pattern)
+ if self._mapped_permission:
+ attributes.append('mapped_permission: "%s"' %
+ self._mapped_permission.pattern)
+ result.write('%s}' % ', '.join(attributes))
+ return result.getvalue()
+
+ def match(self, unit):
+ if unit.mmap:
+ assert unit.region[0] == 'hooked'
+ bucket = unit.bucket_set.get(unit.region[1]['bucket_id'])
+ assert bucket
+ assert bucket.allocator_type == 'mmap'
+
+ stackfunction = bucket.symbolized_joined_stackfunction
+ stacksourcefile = bucket.symbolized_joined_stacksourcefile
+
+ # TODO(dmikurube): Support shared memory.
+ sharedwith = None
+
+ if self._mmap == False: # (self._mmap == None) should go through.
+ return False
+ if (self._backtrace_function and
+ not self._backtrace_function.match(stackfunction)):
+ return False
+ if (self._backtrace_sourcefile and
+ not self._backtrace_sourcefile.match(stacksourcefile)):
+ return False
+ if (self._mapped_pathname and
+ not self._mapped_pathname.match(unit.region[1]['vma']['name'])):
+ return False
+ if (self._mapped_permission and
+ not self._mapped_permission.match(
+ unit.region[1]['vma']['readable'] +
+ unit.region[1]['vma']['writable'] +
+ unit.region[1]['vma']['executable'] +
+ unit.region[1]['vma']['private'])):
+ return False
+ if (self._sharedwith and
+ unit.pageframe and sharedwith not in self._sharedwith):
+ return False
+
+ return True
+
+ else:
+ assert unit.region[0] == 'unhooked'
+
+ # TODO(dmikurube): Support shared memory.
+ sharedwith = None
+
+ if self._mmap == True: # (self._mmap == None) should go through.
+ return False
+ if (self._mapped_pathname and
+ not self._mapped_pathname.match(unit.region[1]['vma']['name'])):
+ return False
+ if (self._mapped_permission and
+ not self._mapped_permission.match(
+ unit.region[1]['vma']['readable'] +
+ unit.region[1]['vma']['writable'] +
+ unit.region[1]['vma']['executable'] +
+ unit.region[1]['vma']['private'])):
+ return False
+ if (self._sharedwith and
+ unit.pageframe and sharedwith not in self._sharedwith):
+ return False
+
+ return True
+
+
+class MallocRule(AbstractRule):
+ """Represents a Rule to match with malloc'ed blocks."""
+ def __init__(self, dct):
+ super(MallocRule, self).__init__(dct)
+ self._backtrace_function = dct.get('backtrace_function', None)
+ if self._backtrace_function:
+ self._backtrace_function = re.compile(self._backtrace_function)
+ self._backtrace_sourcefile = dct.get('backtrace_sourcefile', None)
+ if self._backtrace_sourcefile:
+ self._backtrace_sourcefile = re.compile(self._backtrace_sourcefile)
+ self._typeinfo = dct.get('typeinfo', None)
+ if self._typeinfo:
+ self._typeinfo = re.compile(self._typeinfo)
+
+ def __repr__(self):
+ result = cStringIO.StringIO()
+ result.write('{"%s"=>' % self._name)
+ attributes = []
+ if self._backtrace_function:
+ attributes.append('backtrace_function: "%s"' % self._backtrace_function)
+ if self._typeinfo:
+ attributes.append('typeinfo: "%s"' % self._typeinfo)
+ result.write('%s}' % ', '.join(attributes))
+ return result.getvalue()
+
+ def match(self, unit):
+ assert unit.bucket.allocator_type == 'malloc'
+
+ stackfunction = unit.bucket.symbolized_joined_stackfunction
+ stacksourcefile = unit.bucket.symbolized_joined_stacksourcefile
+ typeinfo = unit.bucket.symbolized_typeinfo
+ if typeinfo.startswith('0x'):
+ typeinfo = unit.bucket.typeinfo_name
+
+ return ((not self._backtrace_function or
+ self._backtrace_function.match(stackfunction)) and
+ (not self._backtrace_sourcefile or
+ self._backtrace_sourcefile.match(stacksourcefile)) and
+ (not self._typeinfo or self._typeinfo.match(typeinfo)))
+
+
+class NoBucketMallocRule(MallocRule):
+ """Represents a Rule that small ignorable units match with."""
+ def __init__(self):
+ super(NoBucketMallocRule, self).__init__({'name': 'tc-no-bucket'})
+ self._no_bucket = True
+
+ @property
+ def no_bucket(self):
+ return self._no_bucket
+
+
+class AbstractSorter(object):
+ """An abstract class for classifying Units with a set of Rules."""
+ def __init__(self, dct):
+ self._type = 'sorter'
+ self._version = dct['version']
+ self._world = dct['world']
+ self._name = dct['name']
+ self._order = dct['order']
+
+ self._rules = []
+ for rule in dct['rules']:
+ if dct['world'] == 'vm':
+ self._rules.append(VMRule(rule))
+ elif dct['world'] == 'malloc':
+ self._rules.append(MallocRule(rule))
+ else:
+ LOGGER.error('Unknown sorter world type')
+
+ def __repr__(self):
+ result = cStringIO.StringIO()
+ result.write('world=%s' % self._world)
+ result.write('order=%s' % self._order)
+ result.write('rules:')
+ for rule in self._rules:
+ result.write(' %s' % rule)
+ return result.getvalue()
+
+ @staticmethod
+ def load(filename):
+ with open(filename) as sorter_f:
+ sorter_dict = json.load(sorter_f)
+ if sorter_dict['world'] == 'vm':
+ return VMSorter(sorter_dict)
+ elif sorter_dict['world'] == 'malloc':
+ return MallocSorter(sorter_dict)
+ else:
+ LOGGER.error('Unknown sorter world type')
+ return None
+
+ @property
+ def world(self):
+ return self._world
+
+ @property
+ def name(self):
+ return self._name
+
+ def find(self, unit):
+ raise NotImplementedError()
+
+ def find_rule(self, name):
+ """Finds a rule whose name is |name|. """
+ for rule in self._rules:
+ if rule.name == name:
+ return rule
+ return None
+
+
+class VMSorter(AbstractSorter):
+ """Represents a Sorter for memory regions on virtual memory."""
+ def __init__(self, dct):
+ assert dct['world'] == 'vm'
+ super(VMSorter, self).__init__(dct)
+
+ def find(self, unit):
+ for rule in self._rules:
+ if rule.match(unit):
+ return rule
+ assert False
+
+
+class MallocSorter(AbstractSorter):
+ """Represents a Sorter for malloc'ed blocks."""
+ def __init__(self, dct):
+ assert dct['world'] == 'malloc'
+ super(MallocSorter, self).__init__(dct)
+ self._no_bucket_rule = NoBucketMallocRule()
+
+ def find(self, unit):
+ if not unit.bucket:
+ return self._no_bucket_rule
+ assert unit.bucket.allocator_type == 'malloc'
+
+ if unit.bucket.component_cache:
+ return unit.bucket.component_cache
+
+ for rule in self._rules:
+ if rule.match(unit):
+ unit.bucket.component_cache = rule
+ return rule
+ assert False
+
+
+class SorterSet(object):
+ """Represents an iterable set of Sorters."""
+ def __init__(self, additional=None, default=None):
+ if not additional:
+ additional = []
+ if not default:
+ default = DEFAULT_SORTERS
+ self._sorters = {}
+ for filename in default + additional:
+ sorter = AbstractSorter.load(filename)
+ if sorter.world not in self._sorters:
+ self._sorters[sorter.world] = []
+ self._sorters[sorter.world].append(sorter)
+
+ def __repr__(self):
+ result = cStringIO.StringIO()
+ result.write(self._sorters)
+ return result.getvalue()
+
+ def __iter__(self):
+ for sorters in self._sorters.itervalues():
+ for sorter in sorters:
+ yield sorter
+
+ def iter_world(self, world):
+ for sorter in self._sorters.get(world, []):
+ yield sorter
« 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