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

Side by Side Diff: tools/metrics/histograms/update_histogram_enum.py

Issue 2835733002: Readdress use_counter_feature_enum issue (Closed)
Patch Set: Codereview: nit + defined custom error Created 3 years, 7 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 unified diff | Download patch
« no previous file with comments | « third_party/WebKit/Source/core/frame/PRESUBMIT.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 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 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Updates enums in histograms.xml file with values read from provided C++ enum. 5 """Updates enums in histograms.xml file with values read from provided C++ enum.
6 6
7 If the file was pretty-printed, the updated version is pretty-printed too. 7 If the file was pretty-printed, the updated version is pretty-printed too.
8 """ 8 """
9 9
10 import logging 10 import logging
11 import os 11 import os
12 import re 12 import re
13 import sys 13 import sys
14 14
15 from xml.dom import minidom 15 from xml.dom import minidom
16 16
17 sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common')) 17 sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common'))
18 import diff_util 18 import diff_util
19 import path_util 19 import path_util
20 20
21 import print_style 21 import print_style
22 22
23 23
24 HISTOGRAMS_PATH = path_util.GetHistogramsFile() 24 HISTOGRAMS_PATH = path_util.GetHistogramsFile()
25 25
26 26
27 class UserError(Exception): 27 class UserError(Exception):
28 def __init__(self, message): 28 def __init__(self, message):
Ilya Sherman 2017/04/26 00:18:50 Hmm, why did you add extra indentation to all of t
lunalu1 2017/04/27 18:31:42 My bad, I didn't realize chromium and Blink had di
29 Exception.__init__(self, message) 29 Exception.__init__(self, message)
30 30
31 @property 31 @property
32 def message(self): 32 def message(self):
33 return self.args[0] 33 return self.args[0]
34
Ilya Sherman 2017/04/26 00:18:50 nit: Please leave two blank lines between top-leve
35 class DuplicatedValue(Exception):
36 """Exception raised for duplicated enum values.
37
38 Attributes:
39 first_label: First enum label that shares the duplicated enum value.
40 second_label: Second enum label that shares the duplicated enum value.
41 """
42 def __init__(self, first_label, second_label):
43 self.first_label = first_label
44 self.second_label = second_label
34 45
35 46
36 def Log(message): 47 def Log(message):
37 logging.info(message) 48 logging.info(message)
38 49
39 50
40 def ReadHistogramValues(filename, start_marker, end_marker, strip_k_prefix): 51 def ReadHistogramValues(filename, start_marker, end_marker, strip_k_prefix):
41 """Returns a dictionary of enum values and a pair of labels that have the same 52 """Creates a dictionary of enum values, read from a C++ file.
42 enum values, read from a C++ file. 53
43 54 Args:
44 Args: 55 filename: The unix-style path (relative to src/) of the file to open.
45 filename: The unix-style path (relative to src/) of the file to open. 56 start_marker: A regex that signifies the start of the enum values.
46 start_marker: A regex that signifies the start of the enum values. 57 end_marker: A regex that signifies the end of the enum values.
47 end_marker: A regex that signifies the end of the enum values. 58 strip_k_prefix: Set to True if enum values are declared as kFoo and the
48 strip_k_prefix: Set to True if enum values are declared as kFoo and the 59 'k' should be stripped.
49 'k' should be stripped. 60
61 Returns:
62 A dict mapping enum labels to the corresponding enum values.
63
64 Raises:
65 DuplicatedValue: An error when two enum labels share the same value.
50 """ 66 """
51 # Read the file as a list of lines 67 # Read the file as a list of lines
52 with open(path_util.GetInputFile(filename)) as f: 68 with open(path_util.GetInputFile(filename)) as f:
53 content = f.readlines() 69 content = f.readlines()
54 70
55 START_REGEX = re.compile(start_marker) 71 START_REGEX = re.compile(start_marker)
56 ITEM_REGEX = re.compile(r'^(\w+)') 72 ITEM_REGEX = re.compile(r'^(\w+)')
57 ITEM_REGEX_WITH_INIT = re.compile(r'(\w+)\s*=\s*(\d*)') 73 ITEM_REGEX_WITH_INIT = re.compile(r'(\w+)\s*=\s*(\d*)')
58 WRAPPED_INIT = re.compile(r'(\d+)') 74 WRAPPED_INIT = re.compile(r'(\d+)')
59 END_REGEX = re.compile(end_marker) 75 END_REGEX = re.compile(end_marker)
60 76
61 iterator = iter(content) 77 iterator = iter(content)
62 # Find the start of the enum 78 # Find the start of the enum
63 for line in iterator: 79 for line in iterator:
64 if START_REGEX.match(line.strip()): 80 if START_REGEX.match(line.strip()):
65 break 81 break
66 82
67 enum_value = 0 83 enum_value = 0
68 result = {} 84 result = {}
69 for line in iterator: 85 for line in iterator:
70 line = line.strip() 86 line = line.strip()
71 # Exit condition: we reached last enum value 87 # Exit condition: we reached last enum value
72 if END_REGEX.match(line): 88 if END_REGEX.match(line):
73 break 89 break
74 # Inside enum: generate new xml entry 90 # Inside enum: generate new xml entry
75 m = ITEM_REGEX_WITH_INIT.match(line) 91 m = ITEM_REGEX_WITH_INIT.match(line)
76 if m: 92 if m:
77 label = m.group(1) 93 label = m.group(1)
78 if m.group(2): 94 if m.group(2):
79 enum_value = int(m.group(2)) 95 enum_value = int(m.group(2))
80 else: 96 else:
81 # Enum name is so long that the value wrapped to the next line 97 # Enum name is so long that the value wrapped to the next line
82 next_line = next(iterator).strip() 98 next_line = next(iterator).strip()
83 enum_value = int(WRAPPED_INIT.match(next_line).group(1)) 99 enum_value = int(WRAPPED_INIT.match(next_line).group(1))
84 else: 100 else:
85 m = ITEM_REGEX.match(line) 101 m = ITEM_REGEX.match(line)
86 if m: 102 if m:
87 label = m.group(1) 103 label = m.group(1)
88 else: 104 else:
89 continue 105 continue
90 # If two enum labels have the same value 106 # If two enum labels have the same value
91 if enum_value in result: 107 if enum_value in result:
92 return result, (result[enum_value], label) 108 raise DuplicatedValue(result[enum_value], label)
93 if strip_k_prefix: 109 if strip_k_prefix:
94 assert label.startswith('k'), "Enum " + label + " should start with 'k'." 110 assert label.startswith('k'), ("Enum " + label +
95 label = label[1:] 111 " should start with 'k'.")
96 result[enum_value] = label 112 label = label[1:]
97 enum_value += 1 113 result[enum_value] = label
98 return result, None 114 enum_value += 1
115 return result
99 116
100 117
101 def CreateEnumItemNode(document, value, label): 118 def CreateEnumItemNode(document, value, label):
102 """Creates an int element to append to an enum.""" 119 """Creates an int element to append to an enum."""
103 item_node = document.createElement('int') 120 item_node = document.createElement('int')
104 item_node.attributes['value'] = str(value) 121 item_node.attributes['value'] = str(value)
105 item_node.attributes['label'] = label 122 item_node.attributes['label'] = label
106 return item_node 123 return item_node
107 124
108 125
109 def UpdateHistogramDefinitions(histogram_enum_name, source_enum_values, 126 def UpdateHistogramDefinitions(histogram_enum_name, source_enum_values,
110 source_enum_path, document): 127 source_enum_path, document):
111 """Updates the enum node named |histogram_enum_name| based on the definition 128 """Updates the enum node named |histogram_enum_name| based on the definition
112 stored in |source_enum_values|. Existing items for which |source_enum_values| 129 stored in |source_enum_values|. Existing items for which
113 doesn't contain any corresponding data will be preserved. |source_enum_path| 130 |source_enum_values| doesn't contain any corresponding data will be
114 will be used to insert a comment. 131 preserved. |source_enum_path| will be used to insert a comment.
115 """ 132 """
116 # Get a dom of <enum name=|histogram_enum_name| ...> node in |document|. 133 # Get a dom of <enum name=|histogram_enum_name| ...> node in |document|.
117 for enum_node in document.getElementsByTagName('enum'): 134 for enum_node in document.getElementsByTagName('enum'):
118 if enum_node.attributes['name'].value == histogram_enum_name: 135 if enum_node.attributes['name'].value == histogram_enum_name:
119 break 136 break
120 else: 137 else:
121 raise UserError('No {0} enum node found'.format(histogram_enum_name)) 138 raise UserError('No {0} enum node found'.format(histogram_enum_name))
122 139
123 new_item_nodes = {} 140 new_item_nodes = {}
124 new_comments = [] 141 new_comments = []
125 142
126 # Add a "Generated from (...)" comment. 143 # Add a "Generated from (...)" comment.
127 new_comments.append( 144 new_comments.append(
128 document.createComment(' Generated from {0} '.format(source_enum_path))) 145 document.createComment(' Generated from {0} '.format(source_enum_path)))
129 146
130 # Create item nodes for each of the enum values. 147 # Create item nodes for each of the enum values.
131 for value, label in source_enum_values.iteritems(): 148 for value, label in source_enum_values.iteritems():
132 new_item_nodes[value] = CreateEnumItemNode(document, value, label) 149 new_item_nodes[value] = CreateEnumItemNode(document, value, label)
133 150
134 # Scan existing nodes in |enum_node| for old values and preserve them. 151 # Scan existing nodes in |enum_node| for old values and preserve them.
135 # - Preserve comments other than the 'Generated from' comment. NOTE: 152 # - Preserve comments other than the 'Generated from' comment. NOTE:
136 # this does not preserve the order of the comments in relation to the 153 # this does not preserve the order of the comments in relation to the
137 # old values. 154 # old values.
138 # - Drop anything else. 155 # - Drop anything else.
139 SOURCE_COMMENT_REGEX = re.compile('^ Generated from ') 156 SOURCE_COMMENT_REGEX = re.compile('^ Generated from ')
140 for child in enum_node.childNodes: 157 for child in enum_node.childNodes:
141 if child.nodeName == 'int': 158 if child.nodeName == 'int':
142 value = int(child.attributes['value'].value) 159 value = int(child.attributes['value'].value)
143 if not source_enum_values.has_key(value): 160 if not source_enum_values.has_key(value):
144 new_item_nodes[value] = child 161 new_item_nodes[value] = child
145 # Preserve existing non-generated comments. 162 # Preserve existing non-generated comments.
146 elif (child.nodeType == minidom.Node.COMMENT_NODE and 163 elif (child.nodeType == minidom.Node.COMMENT_NODE and
147 SOURCE_COMMENT_REGEX.match(child.data) is None): 164 SOURCE_COMMENT_REGEX.match(child.data) is None):
148 new_comments.append(child) 165 new_comments.append(child)
149 166
150 # Update |enum_node|. First, remove everything existing. 167 # Update |enum_node|. First, remove everything existing.
151 while enum_node.hasChildNodes(): 168 while enum_node.hasChildNodes():
152 enum_node.removeChild(enum_node.lastChild) 169 enum_node.removeChild(enum_node.lastChild)
153 170
154 # Add comments at the top. 171 # Add comments at the top.
155 for comment in new_comments: 172 for comment in new_comments:
156 enum_node.appendChild(comment) 173 enum_node.appendChild(comment)
157 174
158 # Add in the new enums. 175 # Add in the new enums.
159 for value in sorted(new_item_nodes.iterkeys()): 176 for value in sorted(new_item_nodes.iterkeys()):
160 enum_node.appendChild(new_item_nodes[value]) 177 enum_node.appendChild(new_item_nodes[value])
161 178
162 179
163 def _GetOldAndUpdatedXml(histogram_enum_name, source_enum_values, 180 def _GetOldAndUpdatedXml(histogram_enum_name, source_enum_values,
164 source_enum_path): 181 source_enum_path):
165 """Reads old histogram from |histogram_enum_name| from |HISTOGRAMS_PATH|, and 182 """Reads old histogram from |histogram_enum_name| from |HISTOGRAMS_PATH|,
166 calculates new histogram from |source_enum_values| from |source_enum_path|, 183 and calculates new histogram from |source_enum_values| from
167 and returns both in XML format. 184 |source_enum_path|, and returns both in XML format.
168 """ 185 """
169 Log('Reading existing histograms from "{0}".'.format(HISTOGRAMS_PATH)) 186 Log('Reading existing histograms from "{0}".'.format(HISTOGRAMS_PATH))
170 with open(HISTOGRAMS_PATH, 'rb') as f: 187 with open(HISTOGRAMS_PATH, 'rb') as f:
171 histograms_doc = minidom.parse(f) 188 histograms_doc = minidom.parse(f)
172 f.seek(0) 189 f.seek(0)
173 xml = f.read() 190 xml = f.read()
174 191
175 Log('Comparing histograms enum with new enum definition.') 192 Log('Comparing histograms enum with new enum definition.')
176 UpdateHistogramDefinitions(histogram_enum_name, source_enum_values, 193 UpdateHistogramDefinitions(histogram_enum_name, source_enum_values,
177 source_enum_path, histograms_doc) 194 source_enum_path, histograms_doc)
178 195
179 new_xml = print_style.GetPrintStyle().PrettyPrintNode(histograms_doc) 196 new_xml = print_style.GetPrintStyle().PrettyPrintNode(histograms_doc)
180 return (xml, new_xml) 197 return (xml, new_xml)
181 198
182 199
183 def HistogramNeedsUpdate(histogram_enum_name, source_enum_path, start_marker, 200 def HistogramNeedsUpdate(histogram_enum_name, source_enum_path, start_marker,
184 end_marker, strip_k_prefix = False): 201 end_marker, strip_k_prefix = False):
185 """Reads a C++ enum from a .h file and does a dry run of updating 202 """Reads a C++ enum from a .h file and does a dry run of updating
186 histograms.xml to match. Returns true if the histograms.xml file would be 203 histograms.xml to match.
187 changed. 204
188 205 Args:
189 Args: 206 histogram_enum_name: The name of the XML <enum> attribute to update.
190 histogram_enum_name: The name of the XML <enum> attribute to update. 207 source_enum_path: A unix-style path, relative to src/, giving
191 source_enum_path: A unix-style path, relative to src/, giving 208 the C++ header file from which to read the enum.
192 the C++ header file from which to read the enum. 209 start_marker: A regular expression that matches the start of the C++
193 start_marker: A regular expression that matches the start of the C++ enum. 210 enum.
194 end_marker: A regular expression that matches the end of the C++ enum. 211 end_marker: A regular expression that matches the end of the C++ enum.
195 strip_k_prefix: Set to True if enum values are declared as kFoo and the 212 strip_k_prefix: Set to True if enum values are declared as kFoo and the
196 'k' should be stripped. 213 'k' should be stripped.
197 """ 214
198 Log('Reading histogram enum definition from "{0}".'.format(source_enum_path)) 215 Returns:
199 source_enum_values, duplicated_values = ReadHistogramValues( 216 A boolean indicating whether the histograms.xml file would be changed.
200 source_enum_path, start_marker, end_marker, strip_k_prefix) 217
201 if duplicated_values: 218 Raises:
202 return False, duplicated_values 219 DuplicatedValue: An error when two enum labels share the same value.
203 220 """
204 (xml, new_xml) = _GetOldAndUpdatedXml(histogram_enum_name, source_enum_values, 221 Log('Reading histogram enum definition from "{0}".'.format(
205 source_enum_path) 222 source_enum_path))
206 return xml != new_xml, None 223 try:
224 source_enum_values, duplicated_values = ReadHistogramValues(
225 source_enum_path, start_marker, end_marker, strip_k_prefix)
226 except DuplicatedValue:
227 raise
Ilya Sherman 2017/04/26 00:18:50 There's no need to use a try/except here. The nat
228
229 (xml, new_xml) = _GetOldAndUpdatedXml(histogram_enum_name,
230 source_enum_values,
231 source_enum_path)
232 return xml != new_xml
207 233
208 234
209 def UpdateHistogramFromDict(histogram_enum_name, source_enum_values, 235 def UpdateHistogramFromDict(histogram_enum_name, source_enum_values,
210 source_enum_path): 236 source_enum_path):
211 """Updates |histogram_enum_name| enum in histograms.xml file with values 237 """Updates |histogram_enum_name| enum in histograms.xml file with values
212 from the {value: 'key'} dictionary |source_enum_values|. A comment is added 238 from the {value: 'key'} dictionary |source_enum_values|. A comment is added
213 to histograms.xml citing that the values in |histogram_enum_name| were 239 to histograms.xml citing that the values in |histogram_enum_name| were
214 sourced from |source_enum_path|. 240 sourced from |source_enum_path|.
215 """ 241 """
216 (xml, new_xml) = _GetOldAndUpdatedXml(histogram_enum_name, source_enum_values, 242 (xml, new_xml) = _GetOldAndUpdatedXml(histogram_enum_name,
217 source_enum_path) 243 source_enum_values,
218 if not diff_util.PromptUserToAcceptDiff( 244 source_enum_path)
219 xml, new_xml, 'Is the updated version acceptable?'): 245 if not diff_util.PromptUserToAcceptDiff(
220 Log('Cancelled.') 246 xml, new_xml, 'Is the updated version acceptable?'):
221 return 247 Log('Cancelled.')
222 248 return
223 with open(HISTOGRAMS_PATH, 'wb') as f: 249
224 f.write(new_xml) 250 with open(HISTOGRAMS_PATH, 'wb') as f:
225 251 f.write(new_xml)
226 Log('Done.') 252
253 Log('Done.')
227 254
228 255
229 def UpdateHistogramEnum(histogram_enum_name, source_enum_path, 256 def UpdateHistogramEnum(histogram_enum_name, source_enum_path,
230 start_marker, end_marker, strip_k_prefix = False): 257 start_marker, end_marker, strip_k_prefix = False):
231 """Reads a C++ enum from a .h file and updates histograms.xml to match. 258 """Reads a C++ enum from a .h file and updates histograms.xml to match.
232 259
233 Args: 260 Args:
234 histogram_enum_name: The name of the XML <enum> attribute to update. 261 histogram_enum_name: The name of the XML <enum> attribute to update.
235 source_enum_path: A unix-style path, relative to src/, giving 262 source_enum_path: A unix-style path, relative to src/, giving
236 the C++ header file from which to read the enum. 263 the C++ header file from which to read the enum.
237 start_marker: A regular expression that matches the start of the C++ enum. 264 start_marker: A regular expression that matches the start of the C++
238 end_marker: A regular expression that matches the end of the C++ enum. 265 enum.
239 strip_k_prefix: Set to True if enum values are declared as kFoo and the 266 end_marker: A regular expression that matches the end of the C++ enum.
240 'k' should be stripped. 267 strip_k_prefix: Set to True if enum values are declared as kFoo and the
241 """ 268 'k' should be stripped.
242 269 """
243 Log('Reading histogram enum definition from "{0}".'.format(source_enum_path)) 270
244 source_enum_values, ignored = ReadHistogramValues(source_enum_path, 271 Log('Reading histogram enum definition from "{0}".'.format(
245 start_marker, end_marker, strip_k_prefix) 272 source_enum_path))
246 273 source_enum_values, ignored = ReadHistogramValues(source_enum_path,
247 UpdateHistogramFromDict(histogram_enum_name, source_enum_values, 274 start_marker,
248 source_enum_path) 275 end_marker,
276 strip_k_prefix)
277
278 UpdateHistogramFromDict(Histogram_enum_name, source_enum_values,
Ilya Sherman 2017/04/26 00:18:50 nit: s/Histogram/histogram (text editor keyboard s
279 source_enum_path)
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/frame/PRESUBMIT.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698