diff options
Diffstat (limited to 'chromium/tools/metrics/histograms/extract_histograms.py')
-rw-r--r-- | chromium/tools/metrics/histograms/extract_histograms.py | 421 |
1 files changed, 0 insertions, 421 deletions
diff --git a/chromium/tools/metrics/histograms/extract_histograms.py b/chromium/tools/metrics/histograms/extract_histograms.py deleted file mode 100644 index 6203e3b71a8..00000000000 --- a/chromium/tools/metrics/histograms/extract_histograms.py +++ /dev/null @@ -1,421 +0,0 @@ -# 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. - -"""Extract histogram names from the description XML file. - -For more information on the format of the XML file, which is self-documenting, -see histograms.xml; however, here is a simple example to get you started. The -XML below will generate the following five histograms: - - HistogramTime - HistogramEnum - HistogramEnum_Chrome - HistogramEnum_IE - HistogramEnum_Firefox - -<histogram-configuration> - -<histograms> - -<histogram name="HistogramTime" units="milliseconds"> - <summary>A brief description.</summary> - <details>This is a more thorough description of this histogram.</details> -</histogram> - -<histogram name="HistogramEnum" enum="MyEnumType"> - <summary>This histogram sports an enum value type.</summary> -</histogram> - -</histograms> - -<enums> - -<enum name="MyEnumType"> - <summary>This is an example enum type, where the values mean little.</summary> - <int value="1" label="FIRST_VALUE">This is the first value.</int> - <int value="2" label="SECOND_VALUE">This is the second value.</int> -</enum> - -</enums> - -<fieldtrials> - -<fieldtrial name="BrowserType"> - <group name="Chrome"/> - <group name="IE"/> - <group name="Firefox"/> - <affected-histogram name="HistogramEnum"/> -</fieldtrial> - -</fieldtrials> - -</histogram-configuration> - -""" - -import copy -import logging -import xml.dom.minidom - - -MAX_FIELDTRIAL_DEPENDENCY_DEPTH = 5 - - -class Error(Exception): - pass - - -def _JoinChildNodes(tag): - """Join child nodes into a single text. - - Applicable to leafs like 'summary' and 'detail'. - - Args: - tag: parent node - - Returns: - a string with concatenated nodes' text representation. - """ - return ''.join(c.toxml() for c in tag.childNodes).strip() - - -def _NormalizeString(s): - """Normalizes a string (possibly of multiple lines) by replacing each - whitespace sequence with a single space. - - Args: - s: The string to normalize, e.g. ' \n a b c\n d ' - - Returns: - The normalized string, e.g. 'a b c d' - """ - return ' '.join(s.split()) - - -def _NormalizeAllAttributeValues(node): - """Recursively normalizes all tag attribute values in the given tree. - - Args: - node: The minidom node to be normalized. - - Returns: - The normalized minidom node. - """ - if node.nodeType == xml.dom.minidom.Node.ELEMENT_NODE: - for a in node.attributes.keys(): - node.attributes[a].value = _NormalizeString(node.attributes[a].value) - - for c in node.childNodes: _NormalizeAllAttributeValues(c) - return node - - -def _ExpandHistogramNameWithFieldTrial(group_name, histogram_name, fieldtrial): - """Creates a new histogram name based on the field trial group. - - Args: - group_name: The name of the field trial group. May be empty. - histogram_name: The name of the histogram. May be of the form - Group.BaseName or BaseName - field_trial: The FieldTrial XML element. - - Returns: - A string with the expanded histogram name. - - Raises: - Error if the expansion can't be done. - """ - if fieldtrial.hasAttribute('separator'): - separator = fieldtrial.getAttribute('separator') - else: - separator = '_' - - if fieldtrial.hasAttribute('ordering'): - ordering = fieldtrial.getAttribute('ordering') - else: - ordering = 'suffix' - if ordering not in ['prefix', 'suffix']: - logging.error('ordering needs to be prefix or suffix, value is %s' % - ordering) - raise Error() - - if not group_name: - return histogram_name - - if ordering == 'suffix': - return histogram_name + separator + group_name - - # For prefixes, the group_name is inserted between the "cluster" and the - # "remainder", e.g. Foo.BarHist expanded with gamma becomes Foo.gamma_BarHist. - sections = histogram_name.split('.') - if len(sections) <= 1: - logging.error( - 'Prefix Field Trial expansions require histogram names which include a ' - 'dot separator. Histogram name is %s, and Field Trial is %s' % - (histogram_name, fieldtrial.getAttribute('name'))) - raise Error() - - cluster = sections[0] + '.' - remainder = '.'.join(sections[1:]) - return cluster + group_name + separator + remainder - - -def _ExtractEnumsFromXmlTree(tree): - """Extract all <enum> nodes in the tree into a dictionary.""" - - enums = {} - have_errors = False - - last_name = None - for enum in tree.getElementsByTagName("enum"): - if enum.getAttribute('type') != 'int': - logging.error('Unknown enum type %s' % enum.getAttribute('type')) - have_errors = True - continue - - name = enum.getAttribute('name') - if last_name is not None and name.lower() < last_name.lower(): - logging.error('Enums %s and %s are not in alphabetical order' - % (last_name, name)) - have_errors = True - last_name = name - - if name in enums: - logging.error('Duplicate enum %s' % name) - have_errors = True - continue - - last_int_value = None - enum_dict = {} - enum_dict['name'] = name - enum_dict['values'] = {} - - for int_tag in enum.getElementsByTagName("int"): - value_dict = {} - int_value = int(int_tag.getAttribute('value')) - if last_int_value is not None and int_value < last_int_value: - logging.error('Enum %s int values %d and %d are not in numerical order' - % (name, last_int_value, int_value)) - have_errors = True - last_int_value = int_value - if int_value in enum_dict['values']: - logging.error('Duplicate enum value %d for enum %s' % (int_value, name)) - have_errors = True - continue - value_dict['label'] = int_tag.getAttribute('label') - value_dict['summary'] = _JoinChildNodes(int_tag) - enum_dict['values'][int_value] = value_dict - - summary_nodes = enum.getElementsByTagName("summary") - if len(summary_nodes) > 0: - enum_dict['summary'] = _NormalizeString(_JoinChildNodes(summary_nodes[0])) - - enums[name] = enum_dict - - return enums, have_errors - - -def _ExtractHistogramsFromXmlTree(tree, enums): - """Extract all <histogram> nodes in the tree into a dictionary.""" - - # Process the histograms. The descriptions can include HTML tags. - histograms = {} - have_errors = False - last_name = None - for histogram in tree.getElementsByTagName("histogram"): - name = histogram.getAttribute('name') - if last_name is not None and name.lower() < last_name.lower(): - logging.error('Histograms %s and %s are not in alphabetical order' - % (last_name, name)) - have_errors = True - last_name = name - if name in histograms: - logging.error('Duplicate histogram definition %s' % name) - have_errors = True - continue - histograms[name] = histogram_entry = {} - - # Find <summary> tag. - summary_nodes = histogram.getElementsByTagName("summary") - if len(summary_nodes) > 0: - histogram_entry['summary'] = _NormalizeString( - _JoinChildNodes(summary_nodes[0])) - else: - histogram_entry['summary'] = 'TBD' - - # Find <obsolete> tag. - obsolete_nodes = histogram.getElementsByTagName("obsolete") - if len(obsolete_nodes) > 0: - reason = _JoinChildNodes(obsolete_nodes[0]) - histogram_entry['obsolete'] = reason - - # Handle units. - if histogram.hasAttribute('units'): - histogram_entry['units'] = histogram.getAttribute('units') - - # Find <details> tag. - details_nodes = histogram.getElementsByTagName("details") - if len(details_nodes) > 0: - histogram_entry['details'] = _NormalizeString( - _JoinChildNodes(details_nodes[0])) - - # Handle enum types. - if histogram.hasAttribute('enum'): - enum_name = histogram.getAttribute('enum') - if not enum_name in enums: - logging.error('Unknown enum %s in histogram %s' % (enum_name, name)) - have_errors = True - else: - histogram_entry['enum'] = enums[enum_name] - - return histograms, have_errors - - -def _UpdateHistogramsWithFieldTrialInformation(tree, histograms): - """Process field trials' tags and combine with affected histograms. - - The histograms dictionary will be updated in-place by adding new histograms - created by combining histograms themselves with field trials targetting these - histograms. - - Args: - tree: XML dom tree. - histograms: a dictinary of histograms previously extracted from the tree; - - Returns: - True if any errors were found. - """ - have_errors = False - - # Verify order of fieldtrial fields first. - last_name = None - for fieldtrial in tree.getElementsByTagName("fieldtrial"): - name = fieldtrial.getAttribute('name') - if last_name is not None and name.lower() < last_name.lower(): - logging.error('Field trials %s and %s are not in alphabetical order' - % (last_name, name)) - have_errors = True - last_name = name - - # Field trials can depend on other field trials, so we need to be careful. - # Make a temporary copy of the list of field trials to use as a queue. - # Field trials whose dependencies have not yet been processed will get - # relegated to the back of the queue to be processed later. - reprocess_queue = [] - def GenerateFieldTrials(): - for f in tree.getElementsByTagName("fieldtrial"): yield 0, f - for r, f in reprocess_queue: yield r, f - - for reprocess_count, fieldtrial in GenerateFieldTrials(): - # Check dependencies first - dependencies_valid = True - affected_histograms = fieldtrial.getElementsByTagName('affected-histogram') - for affected_histogram in affected_histograms: - histogram_name = affected_histogram.getAttribute('name') - if not histogram_name in histograms: - # Base histogram is missing - dependencies_valid = False - missing_dependency = histogram_name - break - if not dependencies_valid: - if reprocess_count < MAX_FIELDTRIAL_DEPENDENCY_DEPTH: - reprocess_queue.append( (reprocess_count + 1, fieldtrial) ) - continue - else: - logging.error('Field trial %s is missing its dependency %s' - % (fieldtrial.getAttribute('name'), - missing_dependency)) - have_errors = True - continue - - name = fieldtrial.getAttribute('name') - groups = fieldtrial.getElementsByTagName('group') - group_labels = {} - for group in groups: - group_labels[group.getAttribute('name')] = group.getAttribute('label') - - last_histogram_name = None - for affected_histogram in affected_histograms: - histogram_name = affected_histogram.getAttribute('name') - if (last_histogram_name is not None - and histogram_name.lower() < last_histogram_name.lower()): - logging.error('Affected histograms %s and %s of field trial %s are not ' - 'in alphabetical order' - % (last_histogram_name, histogram_name, name)) - have_errors = True - last_histogram_name = histogram_name - base_description = histograms[histogram_name] - with_groups = affected_histogram.getElementsByTagName('with-group') - if len(with_groups) > 0: - histogram_groups = with_groups - else: - histogram_groups = groups - for group in histogram_groups: - group_name = group.getAttribute('name') - try: - new_histogram_name = _ExpandHistogramNameWithFieldTrial( - group_name, histogram_name, fieldtrial) - if new_histogram_name != histogram_name: - histograms[new_histogram_name] = copy.deepcopy( - histograms[histogram_name]) - - group_label = group_labels.get(group_name, '') - - if not 'fieldtrial_groups' in histograms[new_histogram_name]: - histograms[new_histogram_name]['fieldtrial_groups'] = [] - histograms[new_histogram_name]['fieldtrial_groups'].append(group_name) - - if not 'fieldtrial_names' in histograms[new_histogram_name]: - histograms[new_histogram_name]['fieldtrial_names'] = [] - histograms[new_histogram_name]['fieldtrial_names'].append(name) - - if not 'fieldtrial_labels' in histograms[new_histogram_name]: - histograms[new_histogram_name]['fieldtrial_labels'] = [] - histograms[new_histogram_name]['fieldtrial_labels'].append( - group_label) - - except Error: - have_errors = True - - return have_errors - - -def ExtractHistogramsFromFile(file_handle): - """Compute the histogram names and descriptions from the XML representation. - - Args: - file_handle: A file or file-like with XML content. - - Returns: - a tuple of (histograms, status) where histograms is a dictionary mapping - histogram names to dictionaries containing histogram descriptions and status - is a boolean indicating if errros were encoutered in processing. - """ - tree = xml.dom.minidom.parse(file_handle) - _NormalizeAllAttributeValues(tree) - - enums, enum_errors = _ExtractEnumsFromXmlTree(tree) - histograms, histogram_errors = _ExtractHistogramsFromXmlTree(tree, enums) - update_errors = _UpdateHistogramsWithFieldTrialInformation(tree, histograms) - - return histograms, enum_errors or histogram_errors or update_errors - - -def ExtractHistograms(filename): - """Load histogram definitions from a disk file. - Args: - filename: a file path to load data from. - - Raises: - Error if the file is not well-formatted. - """ - with open(filename, 'r') as f: - histograms, had_errors = ExtractHistogramsFromFile(f) - if had_errors: - logging.error('Error parsing %s' % filename) - raise Error() - return histograms - - -def ExtractNames(histograms): - return sorted(histograms.keys()) |