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

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

Issue 1143323006: Histograms.xml python script housekeeping (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@bad_message
Patch Set: Fix imports (sadface) Created 5 years, 6 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 | « tools/metrics/common/presubmit_util.py ('k') | tools/metrics/histograms/histogram_ownership.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 commands
14 import extract_histograms
15 import hashlib 13 import hashlib
16 import logging 14 import logging
17 import optparse 15 import optparse
18 import os 16 import os
19 import re 17 import re
18 import subprocess
20 import sys 19 import sys
21 20
21 sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common'))
22 import path_util
23
24 import extract_histograms
25
22 26
23 ADJACENT_C_STRING_REGEX = re.compile(r""" 27 ADJACENT_C_STRING_REGEX = re.compile(r"""
24 (" # Opening quotation mark 28 (" # Opening quotation mark
25 [^"]*) # Literal string contents 29 [^"]*) # Literal string contents
26 " # Closing quotation mark 30 " # Closing quotation mark
27 \s* # Any number of spaces 31 \s* # Any number of spaces
28 " # Another opening quotation mark 32 " # Another opening quotation mark
29 """, re.VERBOSE) 33 """, re.VERBOSE)
30 CONSTANT_REGEX = re.compile(r""" 34 CONSTANT_REGEX = re.compile(r"""
31 (\w*::)? # Optional namespace 35 (\w*::)? # Optional namespace
32 k[A-Z] # Match a constant identifier: 'k' followed by an uppercase letter 36 k[A-Z] # Match a constant identifier: 'k' followed by an uppercase letter
33 \w* # Match the rest of the constant identifier 37 \w* # Match the rest of the constant identifier
34 $ # Make sure there's only the identifier, nothing else 38 $ # Make sure there's only the identifier, nothing else
35 """, re.VERBOSE) 39 """, re.VERBOSE)
36 HISTOGRAM_REGEX = re.compile(r""" 40 HISTOGRAM_REGEX = re.compile(r"""
37 UMA_HISTOGRAM # Match the shared prefix for standard UMA histogram macros 41 UMA_HISTOGRAM # Match the shared prefix for standard UMA histogram macros
38 \w* # Match the rest of the macro name, e.g. '_ENUMERATION' 42 \w* # Match the rest of the macro name, e.g. '_ENUMERATION'
39 \( # Match the opening parenthesis for the macro 43 \( # Match the opening parenthesis for the macro
40 \s* # Match any whitespace -- especially, any newlines 44 \s* # Match any whitespace -- especially, any newlines
41 ([^,)]*) # Capture the first parameter to the macro 45 ([^,)]*) # Capture the first parameter to the macro
42 [,)] # Match the comma/paren that delineates the first parameter 46 [,)] # Match the comma/paren that delineates the first parameter
43 """, re.VERBOSE) 47 """, re.VERBOSE)
44 48
45 49
50 def RunGit(command):
51 """Run a git subcommand, returning its output."""
52 # On Windows, use shell=True to get PATH interpretation.
53 command = ['git'] + command
54 logging.info(' '.join(command))
55 shell = (os.name == 'nt')
56 proc = subprocess.Popen(command, shell=shell, stdout=subprocess.PIPE)
57 out = proc.communicate()[0].strip()
58 return out
59
60
46 class DirectoryNotFoundException(Exception): 61 class DirectoryNotFoundException(Exception):
47 """Base class to distinguish locally defined exceptions from standard ones.""" 62 """Base class to distinguish locally defined exceptions from standard ones."""
48 def __init__(self, msg): 63 def __init__(self, msg):
49 self.msg = msg 64 self.msg = msg
50 65
51 def __str__(self): 66 def __str__(self):
52 return self.msg 67 return self.msg
53 68
54 69
55 def findDefaultRoot():
56 """Find the root of the chromium repo, in case the script is run from the
57 histograms dir.
58
59 Returns:
60 string: path to the src dir of the repo.
61
62 Raises:
63 DirectoryNotFoundException if the target directory cannot be found.
64 """
65 path = os.getcwd()
66 while path:
67 head, tail = os.path.split(path)
68 if tail == 'src':
69 return path
70 if path == head:
71 break
72 path = head
73 raise DirectoryNotFoundException('Could not find src/ dir')
74
75
76 def collapseAdjacentCStrings(string): 70 def collapseAdjacentCStrings(string):
77 """Collapses any adjacent C strings into a single string. 71 """Collapses any adjacent C strings into a single string.
78 72
79 Useful to re-combine strings that were split across multiple lines to satisfy 73 Useful to re-combine strings that were split across multiple lines to satisfy
80 the 80-col restriction. 74 the 80-col restriction.
81 75
82 Args: 76 Args:
83 string: The string to recombine, e.g. '"Foo"\n "bar"' 77 string: The string to recombine, e.g. '"Foo"\n "bar"'
84 78
85 Returns: 79 Returns:
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
123 histogram) 117 histogram)
124 118
125 119
126 def readChromiumHistograms(): 120 def readChromiumHistograms():
127 """Searches the Chromium source for all histogram names. 121 """Searches the Chromium source for all histogram names.
128 122
129 Also prints warnings for any invocations of the UMA_HISTOGRAM_* macros with 123 Also prints warnings for any invocations of the UMA_HISTOGRAM_* macros with
130 names that might vary during a single run of the app. 124 names that might vary during a single run of the app.
131 125
132 Returns: 126 Returns:
133 A set cotaining any found literal histogram names. 127 A set containing any found literal histogram names.
134 """ 128 """
135 logging.info('Scanning Chromium source for histograms...') 129 logging.info('Scanning Chromium source for histograms...')
136 130
137 # Use git grep to find all invocations of the UMA_HISTOGRAM_* macros. 131 # Use git grep to find all invocations of the UMA_HISTOGRAM_* macros.
138 # Examples: 132 # Examples:
139 # 'path/to/foo.cc:420: UMA_HISTOGRAM_COUNTS_100("FooGroup.FooName",' 133 # 'path/to/foo.cc:420: UMA_HISTOGRAM_COUNTS_100("FooGroup.FooName",'
140 # 'path/to/bar.cc:632: UMA_HISTOGRAM_ENUMERATION(' 134 # 'path/to/bar.cc:632: UMA_HISTOGRAM_ENUMERATION('
141 locations = commands.getoutput('git gs UMA_HISTOGRAM').split('\n') 135 locations = RunGit(['gs', 'UMA_HISTOGRAM']).split('\n')
142 filenames = set([location.split(':')[0] for location in locations]) 136 filenames = set([location.split(':')[0] for location in locations])
143 137
144 histograms = set() 138 histograms = set()
145 for filename in filenames: 139 for filename in filenames:
146 contents = '' 140 contents = ''
147 with open(filename, 'r') as f: 141 with open(filename, 'r') as f:
148 contents = f.read() 142 contents = f.read()
149 143
150 matches = set(HISTOGRAM_REGEX.findall(contents)) 144 matches = set(HISTOGRAM_REGEX.findall(contents))
151 for histogram in matches: 145 for histogram in matches:
152 histogram = collapseAdjacentCStrings(histogram) 146 histogram = collapseAdjacentCStrings(histogram)
153 147
154 # Must begin and end with a quotation mark. 148 # Must begin and end with a quotation mark.
155 if histogram[0] != '"' or histogram[-1] != '"': 149 if not histogram or histogram[0] != '"' or histogram[-1] != '"':
156 logNonLiteralHistogram(filename, histogram) 150 logNonLiteralHistogram(filename, histogram)
157 continue 151 continue
158 152
159 # Must not include any quotation marks other than at the beginning or end. 153 # Must not include any quotation marks other than at the beginning or end.
160 histogram_stripped = histogram.strip('"') 154 histogram_stripped = histogram.strip('"')
161 if '"' in histogram_stripped: 155 if '"' in histogram_stripped:
162 logNonLiteralHistogram(filename, histogram) 156 logNonLiteralHistogram(filename, histogram)
163 continue 157 continue
164 158
165 histograms.add(histogram_stripped) 159 histograms.add(histogram_stripped)
(...skipping 19 matching lines...) Expand all
185 name: The string to hash (a histogram name). 179 name: The string to hash (a histogram name).
186 180
187 Returns: 181 Returns:
188 Histogram hash as a string representing a hex number (with leading 0x). 182 Histogram hash as a string representing a hex number (with leading 0x).
189 """ 183 """
190 return '0x' + hashlib.md5(name).hexdigest()[:16] 184 return '0x' + hashlib.md5(name).hexdigest()[:16]
191 185
192 186
193 def main(): 187 def main():
194 # Find default paths. 188 # Find default paths.
195 default_root = findDefaultRoot() 189 default_root = path_util.GetInputFile('/')
196 default_histograms_path = os.path.join( 190 default_histograms_path = path_util.GetInputFile(
197 default_root, 'tools/metrics/histograms/histograms.xml') 191 'tools/metrics/histograms/histograms.xml')
198 default_extra_histograms_path = os.path.join( 192 default_extra_histograms_path = path_util.GetInputFile(
199 default_root, 'tools/histograms/histograms.xml') 193 'tools/histograms/histograms.xml')
200 194
201 # Parse command line options 195 # Parse command line options
202 parser = optparse.OptionParser() 196 parser = optparse.OptionParser()
203 parser.add_option( 197 parser.add_option(
204 '--root-directory', dest='root_directory', default=default_root, 198 '--root-directory', dest='root_directory', default=default_root,
205 help='scan within DIRECTORY for histograms [optional, defaults to "%s"]' % 199 help='scan within DIRECTORY for histograms [optional, defaults to "%s"]' %
206 default_root, 200 default_root,
207 metavar='DIRECTORY') 201 metavar='DIRECTORY')
208 parser.add_option( 202 parser.add_option(
209 '--histograms-file', dest='histograms_file_location', 203 '--histograms-file', dest='histograms_file_location',
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
247 logging.info('Histograms in Chromium but not in XML files:') 241 logging.info('Histograms in Chromium but not in XML files:')
248 logging.info('-------------------------------------------------') 242 logging.info('-------------------------------------------------')
249 for histogram in sorted(unmapped_histograms): 243 for histogram in sorted(unmapped_histograms):
250 logging.info(' %s - %s', histogram, hashHistogramName(histogram)) 244 logging.info(' %s - %s', histogram, hashHistogramName(histogram))
251 else: 245 else:
252 logging.info('Success! No unmapped histograms found.') 246 logging.info('Success! No unmapped histograms found.')
253 247
254 248
255 if __name__ == '__main__': 249 if __name__ == '__main__':
256 main() 250 main()
OLDNEW
« no previous file with comments | « tools/metrics/common/presubmit_util.py ('k') | tools/metrics/histograms/histogram_ownership.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698