Home | History | Annotate | Download | only in histograms
      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 """Updates ExtensionFunctions enum in histograms.xml file with values read from
      6 extension_function_histogram_value.h.
      7 
      8 If the file was pretty-printed, the updated version is pretty-printed too.
      9 """
     10 
     11 import logging
     12 import re
     13 import sys
     14 
     15 from xml.dom import minidom
     16 
     17 from diffutil import PromptUserToAcceptDiff
     18 from pretty_print import PrettyPrintNode
     19 
     20 HISTOGRAMS_PATH = 'histograms.xml'
     21 ENUM_NAME = 'ExtensionFunctions'
     22 
     23 EXTENSION_FUNCTIONS_HISTOGRAM_VALUE_PATH = \
     24   '../../../chrome/browser/extensions/extension_function_histogram_value.h'
     25 ENUM_START_MARKER = "^enum HistogramValue {"
     26 ENUM_END_MARKER = "^ENUM_BOUNDARY"
     27 
     28 
     29 class UserError(Exception):
     30   def __init__(self, message):
     31     Exception.__init__(self, message)
     32 
     33   @property
     34   def message(self):
     35     return self.args[0]
     36 
     37 def ExtractRegexGroup(line, regex):
     38     m = re.match(regex, line)
     39     if m:
     40       return m.group(1)
     41     else:
     42       return None
     43 
     44 
     45 def ReadHistogramValues(filename):
     46   """Returns a list of pairs (label, value) corresponding to HistogramValue.
     47 
     48   Reads the extension_functions_histogram_value.h file, locates the
     49   HistogramValue enum definition and returns a pair for each entry.
     50   """
     51 
     52   # Read the file as a list of lines
     53   with open(filename) as f:
     54     content = f.readlines()
     55 
     56   # Locate the enum definition and collect all entries in it
     57   inside_enum = False # We haven't found the enum definition yet
     58   result = []
     59   for line in content:
     60     line = line.strip()
     61     if inside_enum:
     62       # Exit condition: we reached last enum value
     63       if re.match(ENUM_END_MARKER, line):
     64         inside_enum = False
     65       else:
     66         # Inside enum: generate new xml entry
     67         label = ExtractRegexGroup(line.strip(), "^([\w]+)")
     68         if label:
     69           result.append((label, enum_value))
     70           enum_value += 1
     71     else:
     72       if re.match(ENUM_START_MARKER, line):
     73         inside_enum = True
     74         enum_value = 0 # Start at 'UNKNOWN'
     75   return result
     76 
     77 
     78 def UpdateHistogramDefinitions(histogram_values, document):
     79   """Sets the children of <enum name="ExtensionFunctions" ...> node in
     80   |document| to values generated from policy ids contained in
     81   |policy_templates|.
     82 
     83   Args:
     84     histogram_values: A list of pairs (label, value) defining each extension
     85                       function
     86     document: A minidom.Document object representing parsed histogram
     87               definitions XML file.
     88 
     89   """
     90   # Find ExtensionFunctions enum.
     91   for enum_node in document.getElementsByTagName('enum'):
     92     if enum_node.attributes['name'].value == ENUM_NAME:
     93         extension_functions_enum_node = enum_node
     94         break
     95   else:
     96     raise UserError('No policy enum node found')
     97 
     98   # Remove existing values.
     99   while extension_functions_enum_node.hasChildNodes():
    100     extension_functions_enum_node.removeChild(
    101       extension_functions_enum_node.lastChild)
    102 
    103   # Add a "Generated from (...)" comment
    104   comment = ' Generated from {0} '.format(
    105     EXTENSION_FUNCTIONS_HISTOGRAM_VALUE_PATH)
    106   extension_functions_enum_node.appendChild(document.createComment(comment))
    107 
    108   # Add values generated from policy templates.
    109   for (label, value) in histogram_values:
    110     node = document.createElement('int')
    111     node.attributes['value'] = str(value)
    112     node.attributes['label'] = label
    113     extension_functions_enum_node.appendChild(node)
    114 
    115 def Log(message):
    116   logging.info(message)
    117 
    118 def main():
    119   if len(sys.argv) > 1:
    120     print >>sys.stderr, 'No arguments expected!'
    121     sys.stderr.write(__doc__)
    122     sys.exit(1)
    123 
    124   Log('Reading histogram enum definition from "%s".'
    125       % (EXTENSION_FUNCTIONS_HISTOGRAM_VALUE_PATH))
    126   histogram_values = ReadHistogramValues(
    127     EXTENSION_FUNCTIONS_HISTOGRAM_VALUE_PATH)
    128 
    129   Log('Reading existing histograms from "%s".' % (HISTOGRAMS_PATH))
    130   with open(HISTOGRAMS_PATH, 'rb') as f:
    131     histograms_doc = minidom.parse(f)
    132     f.seek(0)
    133     xml = f.read()
    134 
    135   Log('Comparing histograms enum with new enum definition.')
    136   UpdateHistogramDefinitions(histogram_values, histograms_doc)
    137 
    138   Log('Writing out new histograms file.')
    139   new_xml = PrettyPrintNode(histograms_doc)
    140   if PromptUserToAcceptDiff(xml, new_xml, 'Is the updated version acceptable?'):
    141     with open(HISTOGRAMS_PATH, 'wb') as f:
    142       f.write(new_xml)
    143 
    144   Log('Done.')
    145 
    146 
    147 if __name__ == '__main__':
    148   main()
    149