| 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 |
| 11 """ | 11 """ |
| 12 | 12 |
| 13 import hashlib | 13 import hashlib |
| 14 import logging | 14 import logging |
| 15 import optparse | 15 import optparse |
| 16 import os | 16 import os |
| 17 import re | 17 import re |
| 18 import subprocess | 18 import subprocess |
| 19 import sys | 19 import sys |
| 20 | 20 |
| 21 sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common')) | 21 sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common')) |
| 22 import path_util | 22 import path_util |
| 23 | 23 |
| 24 import extract_histograms | 24 import extract_histograms |
| 25 | 25 |
| 26 | 26 |
| 27 CPP_COMMENT = re.compile(r""" |
| 28 \s* # Optional whitespace |
| 29 (?: # Non-capturing group |
| 30 //.* # C++-style comment |
| 31 \n # Newline |
| 32 | # or |
| 33 /\* # Start C-style comment |
| 34 (?: # Non-capturing group |
| 35 (?!\*/) # Negative lookahead for comment end |
| 36 [\s\S] # Any character including newline |
| 37 )* # Repeated zero or more times |
| 38 \*/ # End C-style comment |
| 39 ) # End group |
| 40 \s* # Optional whitespace |
| 41 """, re.VERBOSE); |
| 27 ADJACENT_C_STRING_REGEX = re.compile(r""" | 42 ADJACENT_C_STRING_REGEX = re.compile(r""" |
| 28 (" # Opening quotation mark | 43 (" # Opening quotation mark |
| 29 [^"]*) # Literal string contents | 44 [^"]*) # Literal string contents |
| 30 " # Closing quotation mark | 45 " # Closing quotation mark |
| 31 \s* # Any number of spaces | 46 \s* # Any number of spaces |
| 32 " # Another opening quotation mark | 47 " # Another opening quotation mark |
| 33 """, re.VERBOSE) | 48 """, re.VERBOSE) |
| 34 CONSTANT_REGEX = re.compile(r""" | 49 CONSTANT_REGEX = re.compile(r""" |
| 35 (\w*::)* # Optional namespace(s) | 50 (\w*::)* # Optional namespace(s) |
| 36 k[A-Z] # Match a constant identifier: 'k' followed by an uppercase letter | 51 k[A-Z] # Match a constant identifier: 'k' followed by an uppercase letter |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 72 | 87 |
| 73 class DirectoryNotFoundException(Exception): | 88 class DirectoryNotFoundException(Exception): |
| 74 """Base class to distinguish locally defined exceptions from standard ones.""" | 89 """Base class to distinguish locally defined exceptions from standard ones.""" |
| 75 def __init__(self, msg): | 90 def __init__(self, msg): |
| 76 self.msg = msg | 91 self.msg = msg |
| 77 | 92 |
| 78 def __str__(self): | 93 def __str__(self): |
| 79 return self.msg | 94 return self.msg |
| 80 | 95 |
| 81 | 96 |
| 97 def removeComments(string): |
| 98 """Remove any comments from an expression, including leading and trailing |
| 99 whitespace. This does not correctly ignore comments embedded in strings, |
| 100 but that shouldn't matter for this script. |
| 101 |
| 102 Args: |
| 103 string: The string to remove comments from, e.g. |
| 104 ' // My histogram\n "My.Important.Counts" ' |
| 105 |
| 106 Returns: |
| 107 The string with comments removed, e.g. '"My.Important.Counts" ' |
| 108 """ |
| 109 return CPP_COMMENT.sub('', string) |
| 110 |
| 111 |
| 82 def collapseAdjacentCStrings(string): | 112 def collapseAdjacentCStrings(string): |
| 83 """Collapses any adjacent C strings into a single string. | 113 """Collapses any adjacent C strings into a single string. |
| 84 | 114 |
| 85 Useful to re-combine strings that were split across multiple lines to satisfy | 115 Useful to re-combine strings that were split across multiple lines to satisfy |
| 86 the 80-col restriction. | 116 the 80-col restriction. |
| 87 | 117 |
| 88 Args: | 118 Args: |
| 89 string: The string to recombine, e.g. '"Foo"\n "bar"' | 119 string: The string to recombine, e.g. '"Foo"\n "bar"' |
| 90 | 120 |
| 91 Returns: | 121 Returns: |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 159 | 189 |
| 160 histograms = set() | 190 histograms = set() |
| 161 location_map = dict() | 191 location_map = dict() |
| 162 for filename in filenames: | 192 for filename in filenames: |
| 163 contents = '' | 193 contents = '' |
| 164 with open(filename, 'r') as f: | 194 with open(filename, 'r') as f: |
| 165 contents = f.read() | 195 contents = f.read() |
| 166 | 196 |
| 167 for match in HISTOGRAM_REGEX.finditer(contents): | 197 for match in HISTOGRAM_REGEX.finditer(contents): |
| 168 histogram = collapseAdjacentCStrings(match.group(1)) | 198 histogram = collapseAdjacentCStrings(match.group(1)) |
| 199 histogram = removeComments(histogram) |
| 169 | 200 |
| 170 # Must begin and end with a quotation mark. | 201 # Must begin and end with a quotation mark. |
| 171 if not histogram or histogram[0] != '"' or histogram[-1] != '"': | 202 if not histogram or histogram[0] != '"' or histogram[-1] != '"': |
| 172 logNonLiteralHistogram(filename, histogram) | 203 logNonLiteralHistogram(filename, histogram) |
| 173 continue | 204 continue |
| 174 | 205 |
| 175 # Must not include any quotation marks other than at the beginning or end. | 206 # Must not include any quotation marks other than at the beginning or end. |
| 176 histogram_stripped = histogram.strip('"') | 207 histogram_stripped = histogram.strip('"') |
| 177 if '"' in histogram_stripped: | 208 if '"' in histogram_stripped: |
| 178 logNonLiteralHistogram(filename, histogram) | 209 logNonLiteralHistogram(filename, histogram) |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 275 logging.info('%s: %s - %s', location_map[histogram], histogram, | 306 logging.info('%s: %s - %s', location_map[histogram], histogram, |
| 276 hashHistogramName(histogram)) | 307 hashHistogramName(histogram)) |
| 277 else: | 308 else: |
| 278 logging.info(' %s - %s', histogram, hashHistogramName(histogram)) | 309 logging.info(' %s - %s', histogram, hashHistogramName(histogram)) |
| 279 else: | 310 else: |
| 280 logging.info('Success! No unmapped histograms found.') | 311 logging.info('Success! No unmapped histograms found.') |
| 281 | 312 |
| 282 | 313 |
| 283 if __name__ == '__main__': | 314 if __name__ == '__main__': |
| 284 main() | 315 main() |
| OLD | NEW |