Roll src/third_party/skia 199ba8e:bb928a0
[chromium-blink-merge.git] / tools / metrics / histograms / update_histogram_enum.py
blob98d70acde103caa884d8de5c8a5cc6de904058a6
1 # Copyright 2014 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.
5 """Updates enums in histograms.xml file with values read from provided C++ enum.
7 If the file was pretty-printed, the updated version is pretty-printed too.
8 """
10 import logging
11 import os
12 import print_style
13 import re
14 import sys
16 from xml.dom import minidom
18 sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common'))
19 from diff_util import PromptUserToAcceptDiff
21 class UserError(Exception):
22 def __init__(self, message):
23 Exception.__init__(self, message)
25 @property
26 def message(self):
27 return self.args[0]
30 def Log(message):
31 logging.info(message)
34 def ReadHistogramValues(filename, start_marker, end_marker):
35 """Reads in values from |filename|, returning a dictionary mapping value to
36 label corresponding to the enum framed by |start_marker| and |end_marker|.
37 """
38 # Read the file as a list of lines
39 with open(filename) as f:
40 content = f.readlines()
42 START_REGEX = re.compile(start_marker)
43 ITEM_REGEX = re.compile(r'^(\w+)')
44 ITEM_REGEX_WITH_INIT = re.compile(r'(\w+)\s*=\s*(\d+)')
45 END_REGEX = re.compile(end_marker)
47 # Locate the enum definition and collect all entries in it
48 inside_enum = False # We haven't found the enum definition yet
49 result = {}
50 for line in content:
51 line = line.strip()
52 if inside_enum:
53 # Exit condition: we reached last enum value
54 if END_REGEX.match(line):
55 inside_enum = False
56 else:
57 # Inside enum: generate new xml entry
58 m = ITEM_REGEX_WITH_INIT.match(line)
59 if m:
60 enum_value = int(m.group(2))
61 label = m.group(1)
62 else:
63 m = ITEM_REGEX.match(line)
64 if m:
65 label = m.group(1)
66 else:
67 continue
68 result[enum_value] = label
69 enum_value += 1
70 else:
71 if START_REGEX.match(line):
72 inside_enum = True
73 enum_value = 0
74 return result
77 def CreateEnumItemNode(document, value, label):
78 """Creates an int element to append to an enum."""
79 item_node = document.createElement('int')
80 item_node.attributes['value'] = str(value)
81 item_node.attributes['label'] = label
82 return item_node
85 def UpdateHistogramDefinitions(histogram_enum_name, source_enum_values,
86 source_enum_path, document):
87 """Updates the enum node named |histogram_enum_name| based on the definition
88 stored in |source_enum_values|. Existing items for which |source_enum_values|
89 doesn't contain any corresponding data will be preserved. |source_enum_path|
90 will be used to insert a comment.
91 """
92 # Get a dom of <enum name=|name| ...> node in |document|.
93 for enum_node in document.getElementsByTagName('enum'):
94 if enum_node.attributes['name'].value == histogram_enum_name:
95 break
96 else:
97 raise UserError('No {0} enum node found'.format(name))
99 new_item_nodes = {}
100 new_comments = []
102 # Add a "Generated from (...)" comment.
103 new_comments.append(
104 document.createComment(' Generated from {0} '.format(source_enum_path)))
106 # Create item nodes for each of the enum values.
107 for value, label in source_enum_values.iteritems():
108 new_item_nodes[value] = CreateEnumItemNode(document, value, label)
110 # Scan existing nodes in |enum_node| for old values and preserve them.
111 # - Preserve comments other than the 'Generated from' comment. NOTE:
112 # this does not preserve the order of the comments in relation to the
113 # old values.
114 # - Drop anything else.
115 SOURCE_COMMENT_REGEX = re.compile('^ Generated from ')
116 for child in enum_node.childNodes:
117 if child.nodeName == 'int':
118 value = int(child.attributes['value'].value)
119 if not source_enum_values.has_key(value):
120 new_item_nodes[value] = child
121 # Preserve existing non-generated comments.
122 elif (child.nodeType == minidom.Node.COMMENT_NODE and
123 SOURCE_COMMENT_REGEX.match(child.data) is None):
124 new_comments.append(child)
126 # Update |enum_node|. First, remove everything existing.
127 while enum_node.hasChildNodes():
128 enum_node.removeChild(enum_node.lastChild)
130 # Add comments at the top.
131 for comment in new_comments:
132 enum_node.appendChild(comment)
134 # Add in the new enums.
135 for value in sorted(new_item_nodes.iterkeys()):
136 enum_node.appendChild(new_item_nodes[value])
139 def UpdateHistogramFromDict(histogram_enum_name, source_enum_values,
140 source_enum_path):
141 """Updates |histogram_enum_name| enum in histograms.xml file with values
142 from the {value: 'key'} dictionary |source_enum_values|. A comment is added
143 to histograms.xml citing that the values in |histogram_enum_name| were
144 sourced from |source_enum_path|.
146 # TODO(ahernandez.miralles): The line below is present in nearly every
147 # file in this directory; factor out into a central location
148 HISTOGRAMS_PATH = 'histograms.xml'
150 Log('Reading existing histograms from "{0}".'.format(HISTOGRAMS_PATH))
151 with open(HISTOGRAMS_PATH, 'rb') as f:
152 histograms_doc = minidom.parse(f)
153 f.seek(0)
154 xml = f.read()
156 Log('Comparing histograms enum with new enum definition.')
157 UpdateHistogramDefinitions(histogram_enum_name, source_enum_values,
158 source_enum_path, histograms_doc)
160 Log('Writing out new histograms file.')
161 new_xml = print_style.GetPrintStyle().PrettyPrintNode(histograms_doc)
162 if not PromptUserToAcceptDiff(
163 xml, new_xml, 'Is the updated version acceptable?'):
164 Log('Cancelled.')
165 return
167 with open(HISTOGRAMS_PATH, 'wb') as f:
168 f.write(new_xml)
170 Log('Done.')
173 def UpdateHistogramEnum(histogram_enum_name, source_enum_path,
174 start_marker, end_marker):
175 """Updates |histogram_enum_name| enum in histograms.xml file with values
176 read from |source_enum_path|, where |start_marker| and |end_marker| indicate
177 the beginning and end of the source enum definition, respectively.
180 Log('Reading histogram enum definition from "{0}".'.format(source_enum_path))
181 source_enum_values = ReadHistogramValues(source_enum_path, start_marker,
182 end_marker)
184 UpdateHistogramFromDict(histogram_enum_name, source_enum_values,
185 source_enum_path)