| OLD | NEW |
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 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 """Scans the Chromium source for histograms that are absent from histograms.xml. | 5 """Scans the Chromium source for histograms that are absent from histograms.xml. |
| 6 | 6 |
| 7 This is a heuristic scan, so a clean run of this script does not guarantee that | 7 This is a heuristic scan, so a clean run of this script does not guarantee that |
| 8 all histograms in the Chromium source are properly mapped. Notably, field | 8 all histograms in the Chromium source are properly mapped. Notably, field |
| 9 trials are entirely ignored by this script. | 9 trials are entirely ignored by this script. |
| 10 | 10 |
| (...skipping 16 matching lines...) Expand all Loading... |
| 27 C_FILENAME = re.compile(r""" | 27 C_FILENAME = re.compile(r""" |
| 28 .* # Anything | 28 .* # Anything |
| 29 \.(cc|cpp|h|mm) # Ending in these extensions | 29 \.(cc|cpp|h|mm) # Ending in these extensions |
| 30 $ # End of string | 30 $ # End of string |
| 31 """, re.VERBOSE) | 31 """, re.VERBOSE) |
| 32 TEST_FILENAME = re.compile(r""" | 32 TEST_FILENAME = re.compile(r""" |
| 33 .* # Anything | 33 .* # Anything |
| 34 test # The word test | 34 test # The word test |
| 35 \. # A literal '.' | 35 \. # A literal '.' |
| 36 """, re.VERBOSE) | 36 """, re.VERBOSE) |
| 37 NON_NEWLINE = re.compile(r'.+') |
| 37 CPP_COMMENT = re.compile(r""" | 38 CPP_COMMENT = re.compile(r""" |
| 38 \s* # Optional whitespace | 39 \s* # Optional whitespace |
| 39 (?: # Non-capturing group | 40 (?: # Non-capturing group |
| 40 //.* # C++-style comment | 41 //.* # C++-style comment |
| 41 \n # Newline | 42 \n # Newline |
| 42 | # or | 43 | # or |
| 43 /\* # Start C-style comment | 44 /\* # Start C-style comment |
| 44 (?: # Non-capturing group | 45 (?: # Non-capturing group |
| 45 (?!\*/) # Negative lookahead for comment end | 46 (?!\*/) # Negative lookahead for comment end |
| 46 [\s\S] # Any character including newline | 47 [\s\S] # Any character including newline |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 121 | 122 |
| 122 class DirectoryNotFoundException(Exception): | 123 class DirectoryNotFoundException(Exception): |
| 123 """Base class to distinguish locally defined exceptions from standard ones.""" | 124 """Base class to distinguish locally defined exceptions from standard ones.""" |
| 124 def __init__(self, msg): | 125 def __init__(self, msg): |
| 125 self.msg = msg | 126 self.msg = msg |
| 126 | 127 |
| 127 def __str__(self): | 128 def __str__(self): |
| 128 return self.msg | 129 return self.msg |
| 129 | 130 |
| 130 | 131 |
| 132 def keepOnlyNewlines(match_object): |
| 133 """Remove everything from a matched string except for the newline characters. |
| 134 Takes a MatchObject argument so that it can be used directly as the repl |
| 135 argument to re.sub(). |
| 136 |
| 137 Args: |
| 138 match_object: A MatchObject referencing the string to be substituted, e.g. |
| 139 ' // My histogram\n ' |
| 140 |
| 141 Returns: |
| 142 The string with non-newlines removed, eg. |
| 143 '\n' |
| 144 """ |
| 145 return NON_NEWLINE.sub('', match_object.group(0)) |
| 146 |
| 147 |
| 131 def removeComments(string): | 148 def removeComments(string): |
| 132 """Remove any comments from an expression, including leading and trailing | 149 """Remove any comments from an expression, including leading and trailing |
| 133 whitespace. This does not correctly ignore comments embedded in strings, | 150 whitespace. This does not correctly ignore comments embedded in strings, but |
| 134 but that shouldn't matter for this script. | 151 that shouldn't matter for this script. Newlines in the removed text are |
| 152 preserved so that line numbers don't change. |
| 135 | 153 |
| 136 Args: | 154 Args: |
| 137 string: The string to remove comments from, e.g. | 155 string: The string to remove comments from, e.g. |
| 138 ' // My histogram\n "My.Important.Counts" ' | 156 ' // My histogram\n "My.Important.Counts" ' |
| 139 | 157 |
| 140 Returns: | 158 Returns: |
| 141 The string with comments removed, e.g. '"My.Important.Counts" ' | 159 The string with comments removed, e.g. '"\nMy.Important.Counts" ' |
| 160 |
| 142 """ | 161 """ |
| 143 return CPP_COMMENT.sub('', string) | 162 return CPP_COMMENT.sub(keepOnlyNewlines, string) |
| 144 | 163 |
| 145 | 164 |
| 146 def collapseAdjacentCStrings(string): | 165 def collapseAdjacentCStrings(string): |
| 147 """Collapses any adjacent C strings into a single string. | 166 """Collapses any adjacent C strings into a single string. |
| 148 | 167 |
| 149 Useful to re-combine strings that were split across multiple lines to satisfy | 168 Useful to re-combine strings that were split across multiple lines to satisfy |
| 150 the 80-col restriction. | 169 the 80-col restriction. |
| 151 | 170 |
| 152 Args: | 171 Args: |
| 153 string: The string to recombine, e.g. '"Foo"\n "bar"' | 172 string: The string to recombine, e.g. '"Foo"\n "bar"' |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 224 if C_FILENAME.match(f) and not TEST_FILENAME.match(f)] | 243 if C_FILENAME.match(f) and not TEST_FILENAME.match(f)] |
| 225 | 244 |
| 226 histograms = set() | 245 histograms = set() |
| 227 location_map = dict() | 246 location_map = dict() |
| 228 unknown_macros = set() | 247 unknown_macros = set() |
| 229 all_suffixes = STANDARD_HISTOGRAM_SUFFIXES | STANDARD_LIKE_SUFFIXES | 248 all_suffixes = STANDARD_HISTOGRAM_SUFFIXES | STANDARD_LIKE_SUFFIXES |
| 230 all_others = OTHER_STANDARD_HISTOGRAMS | OTHER_STANDARD_LIKE_HISTOGRAMS | 249 all_others = OTHER_STANDARD_HISTOGRAMS | OTHER_STANDARD_LIKE_HISTOGRAMS |
| 231 for filename in filenames: | 250 for filename in filenames: |
| 232 contents = '' | 251 contents = '' |
| 233 with open(filename, 'r') as f: | 252 with open(filename, 'r') as f: |
| 234 contents = f.read() | 253 contents = removeComments(f.read()) |
| 235 | 254 |
| 236 for match in HISTOGRAM_REGEX.finditer(contents): | 255 for match in HISTOGRAM_REGEX.finditer(contents): |
| 237 line_number = contents[:match.start()].count('\n') + 1 | 256 line_number = contents[:match.start()].count('\n') + 1 |
| 238 if (match.group(2) not in all_suffixes and | 257 if (match.group(2) not in all_suffixes and |
| 239 match.group(1) not in all_others): | 258 match.group(1) not in all_others): |
| 240 full_macro_name = match.group(1) | 259 full_macro_name = match.group(1) |
| 241 if (full_macro_name not in unknown_macros): | 260 if (full_macro_name not in unknown_macros): |
| 242 logging.warning('%s:%d: Unknown macro name: <%s>' % | 261 logging.warning('%s:%d: Unknown macro name: <%s>' % |
| 243 (filename, line_number, match.group(1))) | 262 (filename, line_number, match.group(1))) |
| 244 unknown_macros.add(full_macro_name) | 263 unknown_macros.add(full_macro_name) |
| 245 | 264 |
| 246 continue | 265 continue |
| 247 | 266 |
| 248 histogram = removeComments(match.group(3)) | 267 histogram = match.group(3).strip() |
| 249 histogram = collapseAdjacentCStrings(histogram) | 268 histogram = collapseAdjacentCStrings(histogram) |
| 250 | 269 |
| 251 # Must begin and end with a quotation mark. | 270 # Must begin and end with a quotation mark. |
| 252 if not histogram or histogram[0] != '"' or histogram[-1] != '"': | 271 if not histogram or histogram[0] != '"' or histogram[-1] != '"': |
| 253 logNonLiteralHistogram(filename, histogram) | 272 logNonLiteralHistogram(filename, histogram) |
| 254 continue | 273 continue |
| 255 | 274 |
| 256 # Must not include any quotation marks other than at the beginning or end. | 275 # Must not include any quotation marks other than at the beginning or end. |
| 257 histogram_stripped = histogram.strip('"') | 276 histogram_stripped = histogram.strip('"') |
| 258 if '"' in histogram_stripped: | 277 if '"' in histogram_stripped: |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 376 logging.warning('No such file: %s', options.extra_histograms_file_location) | 395 logging.warning('No such file: %s', options.extra_histograms_file_location) |
| 377 | 396 |
| 378 if options.output_as_csv: | 397 if options.output_as_csv: |
| 379 output_csv(unmapped_histograms, location_map) | 398 output_csv(unmapped_histograms, location_map) |
| 380 else: | 399 else: |
| 381 output_log(unmapped_histograms, location_map, options.verbose) | 400 output_log(unmapped_histograms, location_map, options.verbose) |
| 382 | 401 |
| 383 | 402 |
| 384 if __name__ == '__main__': | 403 if __name__ == '__main__': |
| 385 main() | 404 main() |
| OLD | NEW |