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

Unified Diff: tools/deep_memory_profiler/lib/policy.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/pageframe.py ('k') | tools/deep_memory_profiler/lib/range_dict.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/deep_memory_profiler/lib/policy.py
diff --git a/tools/deep_memory_profiler/lib/policy.py b/tools/deep_memory_profiler/lib/policy.py
new file mode 100644
index 0000000000000000000000000000000000000000..d7a38977eb702ccb0058f6a5850e01a96a5b7ceb
--- /dev/null
+++ b/tools/deep_memory_profiler/lib/policy.py
@@ -0,0 +1,404 @@
+# 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 json
+import logging
+import os
+import re
+
+
+LOGGER = logging.getLogger('dmprof')
+
+BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+POLICIES_JSON_PATH = os.path.join(BASE_PATH, 'policies.json')
+
+# Heap Profile Policy versions
+
+# POLICY_DEEP_1 DOES NOT include allocation_type columns.
+# mmap regions are distincted w/ mmap frames in the pattern column.
+POLICY_DEEP_1 = 'POLICY_DEEP_1'
+
+# POLICY_DEEP_2 DOES include allocation_type columns.
+# mmap regions are distincted w/ the allocation_type column.
+POLICY_DEEP_2 = 'POLICY_DEEP_2'
+
+# POLICY_DEEP_3 is in JSON format.
+POLICY_DEEP_3 = 'POLICY_DEEP_3'
+
+# POLICY_DEEP_3 contains typeinfo.
+POLICY_DEEP_4 = 'POLICY_DEEP_4'
+
+
+class Rule(object):
+ """Represents one matching rule in a policy file."""
+
+ def __init__(self,
+ name,
+ allocator_type,
+ stackfunction_pattern=None,
+ stacksourcefile_pattern=None,
+ typeinfo_pattern=None,
+ mappedpathname_pattern=None,
+ mappedpermission_pattern=None,
+ sharedwith=None):
+ self._name = name
+ self._allocator_type = allocator_type
+
+ self._stackfunction_pattern = None
+ if stackfunction_pattern:
+ self._stackfunction_pattern = re.compile(
+ stackfunction_pattern + r'\Z')
+
+ self._stacksourcefile_pattern = None
+ if stacksourcefile_pattern:
+ self._stacksourcefile_pattern = re.compile(
+ stacksourcefile_pattern + r'\Z')
+
+ self._typeinfo_pattern = None
+ if typeinfo_pattern:
+ self._typeinfo_pattern = re.compile(typeinfo_pattern + r'\Z')
+
+ self._mappedpathname_pattern = None
+ if mappedpathname_pattern:
+ self._mappedpathname_pattern = re.compile(mappedpathname_pattern + r'\Z')
+
+ self._mappedpermission_pattern = None
+ if mappedpermission_pattern:
+ self._mappedpermission_pattern = re.compile(
+ mappedpermission_pattern + r'\Z')
+
+ self._sharedwith = []
+ if sharedwith:
+ self._sharedwith = sharedwith
+
+ @property
+ def name(self):
+ return self._name
+
+ @property
+ def allocator_type(self):
+ return self._allocator_type
+
+ @property
+ def stackfunction_pattern(self):
+ return self._stackfunction_pattern
+
+ @property
+ def stacksourcefile_pattern(self):
+ return self._stacksourcefile_pattern
+
+ @property
+ def typeinfo_pattern(self):
+ return self._typeinfo_pattern
+
+ @property
+ def mappedpathname_pattern(self):
+ return self._mappedpathname_pattern
+
+ @property
+ def mappedpermission_pattern(self):
+ return self._mappedpermission_pattern
+
+ @property
+ def sharedwith(self):
+ return self._sharedwith
+
+
+class Policy(object):
+ """Represents a policy, a content of a policy file."""
+
+ def __init__(self, rules, version, components):
+ self._rules = rules
+ self._version = version
+ self._components = components
+
+ @property
+ def rules(self):
+ return self._rules
+
+ @property
+ def version(self):
+ return self._version
+
+ @property
+ def components(self):
+ return self._components
+
+ def find_rule(self, component_name):
+ """Finds a rule whose name is |component_name|. """
+ for rule in self._rules:
+ if rule.name == component_name:
+ return rule
+ return None
+
+ def find_malloc(self, bucket):
+ """Finds a matching component name which a given |bucket| belongs to.
+
+ Args:
+ bucket: A Bucket object to be searched for.
+
+ Returns:
+ A string representing a component name.
+ """
+ assert not bucket or bucket.allocator_type == 'malloc'
+
+ if not bucket:
+ return 'no-bucket'
+ if bucket.component_cache:
+ return bucket.component_cache
+
+ stackfunction = bucket.symbolized_joined_stackfunction
+ stacksourcefile = bucket.symbolized_joined_stacksourcefile
+ typeinfo = bucket.symbolized_typeinfo
+ if typeinfo.startswith('0x'):
+ typeinfo = bucket.typeinfo_name
+
+ for rule in self._rules:
+ if (rule.allocator_type == 'malloc' and
+ (not rule.stackfunction_pattern or
+ rule.stackfunction_pattern.match(stackfunction)) and
+ (not rule.stacksourcefile_pattern or
+ rule.stacksourcefile_pattern.match(stacksourcefile)) and
+ (not rule.typeinfo_pattern or rule.typeinfo_pattern.match(typeinfo))):
+ bucket.component_cache = rule.name
+ return rule.name
+
+ assert False
+
+ def find_mmap(self, region, bucket_set,
+ pageframe=None, group_pfn_counts=None):
+ """Finds a matching component which a given mmap |region| belongs to.
+
+ It uses |bucket_set| to match with backtraces. If |pageframe| is given,
+ it considers memory sharing among processes.
+
+ NOTE: Don't use Bucket's |component_cache| for mmap regions because they're
+ classified not only with bucket information (mappedpathname for example).
+
+ Args:
+ region: A tuple representing a memory region.
+ bucket_set: A BucketSet object to look up backtraces.
+ pageframe: A PageFrame object representing a pageframe maybe including
+ a pagecount.
+ group_pfn_counts: A dict mapping a PFN to the number of times the
+ the pageframe is mapped by the known "group (Chrome)" processes.
+
+ Returns:
+ A string representing a component name.
+ """
+ assert region[0] == 'hooked'
+ bucket = bucket_set.get(region[1]['bucket_id'])
+ assert not bucket or bucket.allocator_type == 'mmap'
+
+ if not bucket:
+ return 'no-bucket', None
+
+ stackfunction = bucket.symbolized_joined_stackfunction
+ stacksourcefile = bucket.symbolized_joined_stacksourcefile
+ sharedwith = self._categorize_pageframe(pageframe, group_pfn_counts)
+
+ for rule in self._rules:
+ if (rule.allocator_type == 'mmap' and
+ (not rule.stackfunction_pattern or
+ rule.stackfunction_pattern.match(stackfunction)) and
+ (not rule.stacksourcefile_pattern or
+ rule.stacksourcefile_pattern.match(stacksourcefile)) and
+ (not rule.mappedpathname_pattern or
+ rule.mappedpathname_pattern.match(region[1]['vma']['name'])) and
+ (not rule.mappedpermission_pattern or
+ rule.mappedpermission_pattern.match(
+ region[1]['vma']['readable'] +
+ region[1]['vma']['writable'] +
+ region[1]['vma']['executable'] +
+ region[1]['vma']['private'])) and
+ (not rule.sharedwith or
+ not pageframe or sharedwith in rule.sharedwith)):
+ return rule.name, bucket
+
+ assert False
+
+ def find_unhooked(self, region, pageframe=None, group_pfn_counts=None):
+ """Finds a matching component which a given unhooked |region| belongs to.
+
+ If |pageframe| is given, it considers memory sharing among processes.
+
+ Args:
+ region: A tuple representing a memory region.
+ pageframe: A PageFrame object representing a pageframe maybe including
+ a pagecount.
+ group_pfn_counts: A dict mapping a PFN to the number of times the
+ the pageframe is mapped by the known "group (Chrome)" processes.
+
+ Returns:
+ A string representing a component name.
+ """
+ assert region[0] == 'unhooked'
+ sharedwith = self._categorize_pageframe(pageframe, group_pfn_counts)
+
+ for rule in self._rules:
+ if (rule.allocator_type == 'unhooked' and
+ (not rule.mappedpathname_pattern or
+ rule.mappedpathname_pattern.match(region[1]['vma']['name'])) and
+ (not rule.mappedpermission_pattern or
+ rule.mappedpermission_pattern.match(
+ region[1]['vma']['readable'] +
+ region[1]['vma']['writable'] +
+ region[1]['vma']['executable'] +
+ region[1]['vma']['private'])) and
+ (not rule.sharedwith or
+ not pageframe or sharedwith in rule.sharedwith)):
+ return rule.name
+
+ assert False
+
+ @staticmethod
+ def load(filename, filetype):
+ """Loads a policy file of |filename| in a |format|.
+
+ Args:
+ filename: A filename to be loaded.
+ filetype: A string to specify a type of the file. Only 'json' is
+ supported for now.
+
+ Returns:
+ A loaded Policy object.
+ """
+ with open(os.path.join(BASE_PATH, filename)) as policy_f:
+ return Policy.parse(policy_f, filetype)
+
+ @staticmethod
+ def parse(policy_f, filetype):
+ """Parses a policy file content in a |format|.
+
+ Args:
+ policy_f: An IO object to be loaded.
+ filetype: A string to specify a type of the file. Only 'json' is
+ supported for now.
+
+ Returns:
+ A loaded Policy object.
+ """
+ if filetype == 'json':
+ return Policy._parse_json(policy_f)
+ else:
+ return None
+
+ @staticmethod
+ def _parse_json(policy_f):
+ """Parses policy file in json format.
+
+ A policy file contains component's names and their stacktrace pattern
+ written in regular expression. Those patterns are matched against each
+ symbols of each stacktraces in the order written in the policy file
+
+ Args:
+ policy_f: A File/IO object to read.
+
+ Returns:
+ A loaded policy object.
+ """
+ policy = json.load(policy_f)
+
+ rules = []
+ for rule in policy['rules']:
+ stackfunction = rule.get('stackfunction') or rule.get('stacktrace')
+ stacksourcefile = rule.get('stacksourcefile')
+ rules.append(Rule(
+ rule['name'],
+ rule['allocator'], # allocator_type
+ stackfunction,
+ stacksourcefile,
+ rule['typeinfo'] if 'typeinfo' in rule else None,
+ rule.get('mappedpathname'),
+ rule.get('mappedpermission'),
+ rule.get('sharedwith')))
+
+ return Policy(rules, policy['version'], policy['components'])
+
+ @staticmethod
+ def _categorize_pageframe(pageframe, group_pfn_counts):
+ """Categorizes a pageframe based on its sharing status.
+
+ Returns:
+ 'private' if |pageframe| is not shared with other processes. 'group'
+ if |pageframe| is shared only with group (Chrome-related) processes.
+ 'others' if |pageframe| is shared with non-group processes.
+ """
+ if not pageframe:
+ return 'private'
+
+ if pageframe.pagecount:
+ if pageframe.pagecount == 1:
+ return 'private'
+ elif pageframe.pagecount <= group_pfn_counts.get(pageframe.pfn, 0) + 1:
+ return 'group'
+ else:
+ return 'others'
+ else:
+ if pageframe.pfn in group_pfn_counts:
+ return 'group'
+ else:
+ return 'private'
+
+
+class PolicySet(object):
+ """Represents a set of policies."""
+
+ def __init__(self, policy_directory):
+ self._policy_directory = policy_directory
+
+ @staticmethod
+ def load(labels=None):
+ """Loads a set of policies via the "default policy directory".
+
+ The "default policy directory" contains pairs of policies and their labels.
+ For example, a policy "policy.l0.json" is labeled "l0" in the default
+ policy directory "policies.json".
+
+ All policies in the directory are loaded by default. Policies can be
+ limited by |labels|.
+
+ Args:
+ labels: An array that contains policy labels to be loaded.
+
+ Returns:
+ A PolicySet object.
+ """
+ default_policy_directory = PolicySet._load_default_policy_directory()
+ if labels:
+ specified_policy_directory = {}
+ for label in labels:
+ if label in default_policy_directory:
+ specified_policy_directory[label] = default_policy_directory[label]
+ # TODO(dmikurube): Load an un-labeled policy file.
+ return PolicySet._load_policies(specified_policy_directory)
+ else:
+ return PolicySet._load_policies(default_policy_directory)
+
+ def __len__(self):
+ return len(self._policy_directory)
+
+ def __iter__(self):
+ for label in self._policy_directory:
+ yield label
+
+ def __getitem__(self, label):
+ return self._policy_directory[label]
+
+ @staticmethod
+ def _load_default_policy_directory():
+ with open(POLICIES_JSON_PATH, mode='r') as policies_f:
+ default_policy_directory = json.load(policies_f)
+ return default_policy_directory
+
+ @staticmethod
+ def _load_policies(directory):
+ LOGGER.info('Loading policy files.')
+ policies = {}
+ for label in directory:
+ LOGGER.info(' %s: %s' % (label, directory[label]['file']))
+ loaded = Policy.load(directory[label]['file'], directory[label]['format'])
+ if loaded:
+ policies[label] = loaded
+ return PolicySet(policies)
« no previous file with comments | « tools/deep_memory_profiler/lib/pageframe.py ('k') | tools/deep_memory_profiler/lib/range_dict.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698