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

Side by Side Diff: chrome/tools/extract_actions.py

Issue 7314020: Update UMA user actions parsing, primarily to include WebUI metrics. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Update regex Created 9 years, 4 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 | Annotate | Revision Log
« no previous file with comments | « chrome/tools/chromeactions.txt ('k') | content/browser/renderer_host/render_widget_host.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Extract UserMetrics "actions" strings from the Chrome source. 6 """Extract UserMetrics "actions" strings from the Chrome source.
7 7
8 This program generates the list of known actions we expect to see in the 8 This program generates the list of known actions we expect to see in the
9 user behavior logs. It walks the Chrome source, looking for calls to 9 user behavior logs. It walks the Chrome source, looking for calls to
10 UserMetrics functions, extracting actions and warning on improper calls, 10 UserMetrics functions, extracting actions and warning on improper calls,
11 as well as generating the lists of possible actions in situations where 11 as well as generating the lists of possible actions in situations where
12 there are many possible actions. 12 there are many possible actions.
13 13
14 See also: 14 See also:
15 chrome/browser/user_metrics.h 15 chrome/browser/user_metrics.h
16 http://wiki.corp.google.com/twiki/bin/view/Main/ChromeUserExperienceMetrics 16 http://wiki.corp.google.com/twiki/bin/view/Main/ChromeUserExperienceMetrics
17 17
18 If run with a "--hash" argument, chromeactions.txt will be updated. 18 If run with a "--hash" argument, chromeactions.txt will be updated.
19 """ 19 """
20 20
21 __author__ = 'evanm (Evan Martin)' 21 __author__ = 'evanm (Evan Martin)'
22 22
23 import hashlib
24 from HTMLParser import HTMLParser
23 import os 25 import os
24 import re 26 import re
25 import sys 27 import sys
26 import hashlib
27 28
28 sys.path.insert(1, os.path.join(sys.path[0], '..', '..', 'tools', 'python')) 29 sys.path.insert(1, os.path.join(sys.path[0], '..', '..', 'tools', 'python'))
29 from google import path_utils 30 from google import path_utils
30 31
31 # Files that are known to use UserMetrics::RecordComputedAction(), which means 32 # Files that are known to use UserMetrics::RecordComputedAction(), which means
32 # they require special handling code in this script. 33 # they require special handling code in this script.
33 # To add a new file, add it to this list and add the appropriate logic to 34 # To add a new file, add it to this list and add the appropriate logic to
34 # generate the known actions to AddComputedActions() below. 35 # generate the known actions to AddComputedActions() below.
35 KNOWN_COMPUTED_USERS = ( 36 KNOWN_COMPUTED_USERS = (
36 'back_forward_menu_model.cc', 37 'back_forward_menu_model.cc',
37 'options_page_view.cc', 38 'options_page_view.cc',
38 'render_view_host.cc', # called using webkit identifiers 39 'render_view_host.cc', # called using webkit identifiers
39 'user_metrics.cc', # method definition 40 'user_metrics.cc', # method definition
40 'new_tab_ui.cc', # most visited clicks 1-9 41 'new_tab_ui.cc', # most visited clicks 1-9
41 'extension_metrics_module.cc', # extensions hook for user metrics 42 'extension_metrics_module.cc', # extensions hook for user metrics
42 'safe_browsing_blocking_page.cc', # various interstitial types and actions 43 'safe_browsing_blocking_page.cc', # various interstitial types and actions
43 'language_options_handler.cc', # languages and input methods in chrome os 44 'language_options_handler_common.cc', # languages and input methods in CrOS
45 'cros_language_options_handler.cc', # languages and input methods in CrOS
44 'about_flags.cc', # do not generate a warning; see AddAboutFlagsActions() 46 'about_flags.cc', # do not generate a warning; see AddAboutFlagsActions()
47 'external_metrics.cc', # see AddChromeOSActions()
48 'core_options_handler.cc' # see AddWebUIActions()
45 ) 49 )
46 50
47 # Language codes used in Chrome. The list should be updated when a new 51 # Language codes used in Chrome. The list should be updated when a new
48 # language is added to app/l10n_util.cc, as follows: 52 # language is added to app/l10n_util.cc, as follows:
49 # 53 #
50 # % (cat app/l10n_util.cc | \ 54 # % (cat app/l10n_util.cc | \
51 # perl -n0e 'print $1 if /kAcceptLanguageList.*?\{(.*?)\}/s' | \ 55 # perl -n0e 'print $1 if /kAcceptLanguageList.*?\{(.*?)\}/s' | \
52 # perl -nle 'print $1, if /"(.*)"/'; echo 'es-419') | \ 56 # perl -nle 'print $1, if /"(.*)"/'; echo 'es-419') | \
53 # sort | perl -pe "s/(.*)\n/'\$1', /" | \ 57 # sort | perl -pe "s/(.*)\n/'\$1', /" | \
54 # fold -w75 -s | perl -pe 's/^/ /;s/ $//'; echo 58 # fold -w75 -s | perl -pe 's/^/ /;s/ $//'; echo
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after
215 """Grep a source file for calls to UserMetrics functions. 219 """Grep a source file for calls to UserMetrics functions.
216 220
217 Arguments: 221 Arguments:
218 path: path to the file 222 path: path to the file
219 actions: set of actions to add to 223 actions: set of actions to add to
220 """ 224 """
221 global number_of_files_total 225 global number_of_files_total
222 number_of_files_total = number_of_files_total + 1 226 number_of_files_total = number_of_files_total + 1
223 # we look for the UserMetricsAction structure constructor 227 # we look for the UserMetricsAction structure constructor
224 # this should be on one line 228 # this should be on one line
225 action_re = re.compile(r'UserMetricsAction\("([^"]*)') 229 action_re = re.compile(r'[^a-zA-Z]UserMetricsAction\("([^"]*)')
230 malformed_action_re = re.compile(r'[^a-zA-Z]UserMetricsAction\([^"]')
226 computed_action_re = re.compile(r'UserMetrics::RecordComputedAction') 231 computed_action_re = re.compile(r'UserMetrics::RecordComputedAction')
227 line_number = 0 232 line_number = 0
228 for line in open(path): 233 for line in open(path):
229 line_number = line_number + 1 234 line_number = line_number + 1
230 match = action_re.search(line) 235 match = action_re.search(line)
231 if match: # Plain call to RecordAction 236 if match: # Plain call to RecordAction
232 actions.add(match.group(1)) 237 actions.add(match.group(1))
238 elif malformed_action_re.search(line):
239 # Warn if this line is using RecordAction incorrectly.
240 print >>sys.stderr, ('WARNING: %s has malformed call to RecordAction'
241 ' at %d' % (path, line_number))
233 elif computed_action_re.search(line): 242 elif computed_action_re.search(line):
234 # Warn if this file shouldn't be calling RecordComputedAction. 243 # Warn if this file shouldn't be calling RecordComputedAction.
235 if os.path.basename(path) not in KNOWN_COMPUTED_USERS: 244 if os.path.basename(path) not in KNOWN_COMPUTED_USERS:
236 print >>sys.stderr, 'WARNING: {0} has RecordComputedAction at {1}'.\ 245 print >>sys.stderr, ('WARNING: %s has RecordComputedAction at %d' %
237 format(path, line_number) 246 (path, line_number))
238 247
239 def WalkDirectory(root_path, actions): 248 class WebUIActionsParser(HTMLParser):
249 """Parses an HTML file, looking for all tags with a 'metric' attribute.
250 Adds user actions corresponding to any metrics found.
251
252 Arguments:
253 actions: set of actions to add to
254 """
255 def __init__(self, actions):
256 HTMLParser.__init__(self)
257 self.actions = actions
258
259 def handle_starttag(self, tag, attrs):
260 # We only care to examine tags that have a 'metric' attribute.
261 attrs = dict(attrs)
262 if not 'metric' in attrs:
263 return
264
265 # Boolean metrics have two corresponding actions. All other metrics have
266 # just one corresponding action. By default, we check the 'dataType'
267 # attribute.
268 is_boolean = ('dataType' in attrs and attrs['dataType'] == 'boolean')
269 if 'type' in attrs and attrs['type'] in ('checkbox', 'radio'):
270 if attrs['type'] == 'checkbox':
271 # Checkboxes are boolean by default. However, their 'value-type' can
272 # instead be set to 'integer'.
273 if 'value-type' not in attrs or attrs['value-type'] in ['', 'boolean']:
274 is_boolean = True
275 else:
276 # Radio buttons are boolean if and only if their values are 'true' or
277 # 'false'.
278 assert(attrs['type'] == 'radio')
279 if 'value' in attrs and attrs['value'] in ['true', 'false']:
280 is_boolean = True
281
282 if is_boolean:
283 self.actions.add(attrs['metric'] + '_Enable')
284 self.actions.add(attrs['metric'] + '_Disable')
285 else:
286 self.actions.add(attrs['metric'])
287
288 def GrepForWebUIActions(path, actions):
289 """Grep a WebUI source file for elements with associated metrics.
290
291 Arguments:
292 path: path to the file
293 actions: set of actions to add to
294 """
295 parser = WebUIActionsParser(actions)
296 parser.feed(open(path).read())
297 parser.close()
298
299 def WalkDirectory(root_path, actions, extensions, callback):
240 for path, dirs, files in os.walk(root_path): 300 for path, dirs, files in os.walk(root_path):
241 if '.svn' in dirs: 301 if '.svn' in dirs:
242 dirs.remove('.svn') 302 dirs.remove('.svn')
243 for file in files: 303 for file in files:
244 ext = os.path.splitext(file)[1] 304 ext = os.path.splitext(file)[1]
245 if ext in ('.cc', '.mm', '.c', '.m'): 305 if ext in extensions:
246 GrepForActions(os.path.join(path, file), actions) 306 callback(os.path.join(path, file), actions)
307
308 def AddLiteralActions(actions):
309 """Add literal actions specified via calls to UserMetrics functions.
310
311 Arguments:
312 actions: set of actions to add to.
313 """
314 EXTENSIONS = ('.cc', '.mm', '.c', '.m')
315
316 # Walk the source tree to process all .cc files.
317 chrome_root = os.path.join(path_utils.ScriptDir(), '..')
318 WalkDirectory(chrome_root, actions, EXTENSIONS, GrepForActions)
319 content_root = os.path.join(path_utils.ScriptDir(), '..', '..', 'content')
320 WalkDirectory(content_root, actions, EXTENSIONS, GrepForActions)
321 webkit_root = os.path.join(path_utils.ScriptDir(), '..', '..', 'webkit')
322 WalkDirectory(os.path.join(webkit_root, 'glue'), actions, EXTENSIONS,
323 GrepForActions)
324 WalkDirectory(os.path.join(webkit_root, 'port'), actions, EXTENSIONS,
325 GrepForActions)
326
327 def AddWebUIActions(actions):
328 """Add user actions defined in WebUI files.
329
330 Arguments:
331 actions: set of actions to add to.
332 """
333 resources_root = os.path.join(path_utils.ScriptDir(), '..', 'browser',
334 'resources')
335 WalkDirectory(resources_root, actions, ('.html'), GrepForWebUIActions)
247 336
248 def main(argv): 337 def main(argv):
249 if '--hash' in argv: 338 if '--hash' in argv:
250 hash_output = True 339 hash_output = True
251 else: 340 else:
252 hash_output = False 341 hash_output = False
253 print >>sys.stderr, "WARNING: If you added new UMA tags, you must" + \ 342 print >>sys.stderr, "WARNING: If you added new UMA tags, you must" + \
254 " use the --hash option to update chromeactions.txt." 343 " use the --hash option to update chromeactions.txt."
255 # if we do a hash output, we want to only append NEW actions, and we know 344 # if we do a hash output, we want to only append NEW actions, and we know
256 # the file we want to work on 345 # the file we want to work on
257 actions = set() 346 actions = set()
258 347
259 chromeactions_path = os.path.join(path_utils.ScriptDir(), "chromeactions.txt") 348 chromeactions_path = os.path.join(path_utils.ScriptDir(), "chromeactions.txt")
260 349
261 if hash_output: 350 if hash_output:
262 f = open(chromeactions_path) 351 f = open(chromeactions_path)
263 for line in f: 352 for line in f:
264 part = line.rpartition("\t") 353 part = line.rpartition("\t")
265 part = part[2].strip() 354 part = part[2].strip()
266 actions.add(part) 355 actions.add(part)
267 f.close() 356 f.close()
268 357
269 358
270 AddComputedActions(actions) 359 AddComputedActions(actions)
271 # TODO(fmantek): bring back webkit editor actions. 360 # TODO(fmantek): bring back webkit editor actions.
272 # AddWebKitEditorActions(actions) 361 # AddWebKitEditorActions(actions)
273 AddAboutFlagsActions(actions) 362 AddAboutFlagsActions(actions)
363 AddWebUIActions(actions)
274 364
275 # Walk the source tree to process all .cc files. 365 AddLiteralActions(actions)
276 chrome_root = os.path.join(path_utils.ScriptDir(), '..')
277 WalkDirectory(chrome_root, actions)
278 content_root = os.path.join(path_utils.ScriptDir(), '..', '..', 'content')
279 WalkDirectory(content_root, actions)
280 webkit_root = os.path.join(path_utils.ScriptDir(), '..', '..', 'webkit')
281 WalkDirectory(os.path.join(webkit_root, 'glue'), actions)
282 WalkDirectory(os.path.join(webkit_root, 'port'), actions)
283 366
284 # print "Scanned {0} number of files".format(number_of_files_total) 367 # print "Scanned {0} number of files".format(number_of_files_total)
285 # print "Found {0} entries".format(len(actions)) 368 # print "Found {0} entries".format(len(actions))
286 369
287 AddClosedSourceActions(actions) 370 AddClosedSourceActions(actions)
288 AddChromeOSActions(actions) 371 AddChromeOSActions(actions)
289 372
290 if hash_output: 373 if hash_output:
291 f = open(chromeactions_path, "w") 374 f = open(chromeactions_path, "w")
292 375
293 376
294 # Print out the actions as a sorted list. 377 # Print out the actions as a sorted list.
295 for action in sorted(actions): 378 for action in sorted(actions):
296 if hash_output: 379 if hash_output:
297 hash = hashlib.md5() 380 hash = hashlib.md5()
298 hash.update(action) 381 hash.update(action)
299 print >>f, '0x%s\t%s' % (hash.hexdigest()[:16], action) 382 print >>f, '0x%s\t%s' % (hash.hexdigest()[:16], action)
300 else: 383 else:
301 print action 384 print action
302 385
303 if hash_output: 386 if hash_output:
304 print "Done. Do not forget to add chromeactions.txt to your changelist" 387 print "Done. Do not forget to add chromeactions.txt to your changelist"
305 388
306 if '__main__' == __name__: 389 if '__main__' == __name__:
307 main(sys.argv) 390 main(sys.argv)
OLDNEW
« no previous file with comments | « chrome/tools/chromeactions.txt ('k') | content/browser/renderer_host/render_widget_host.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698