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

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: Fix compile Created 9 years, 5 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
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 219
216 Arguments: 220 Arguments:
217 path: path to the file 221 path: path to the file
218 actions: set of actions to add to 222 actions: set of actions to add to
219 """ 223 """
220 global number_of_files_total 224 global number_of_files_total
221 number_of_files_total = number_of_files_total + 1 225 number_of_files_total = number_of_files_total + 1
222 # we look for the UserMetricsAction structure constructor 226 # we look for the UserMetricsAction structure constructor
223 # this should be on one line 227 # this should be on one line
224 action_re = re.compile(r'UserMetricsAction\("([^"]*)') 228 action_re = re.compile(r'UserMetricsAction\("([^"]*)')
229 malformed_action_re = re.compile(r'[^a-zA-Z]UserMetricsAction\([^"]')
225 computed_action_re = re.compile(r'UserMetrics::RecordComputedAction') 230 computed_action_re = re.compile(r'UserMetrics::RecordComputedAction')
226 line_number = 0 231 line_number = 0
227 for line in open(path): 232 for line in open(path):
228 line_number = line_number + 1 233 line_number = line_number + 1
229 match = action_re.search(line) 234 match = action_re.search(line)
230 if match: # Plain call to RecordAction 235 if match: # Plain call to RecordAction
231 actions.add(match.group(1)) 236 actions.add(match.group(1))
237 elif malformed_action_re.search(line):
238 # Warn if this line is using RecordAction incorrectly.
239 print >>sys.stderr, ('WARNING: %s has malformed call to RecordAction'
240 ' at %d' % (path, line_number))
232 elif computed_action_re.search(line): 241 elif computed_action_re.search(line):
233 # Warn if this file shouldn't be calling RecordComputedAction. 242 # Warn if this file shouldn't be calling RecordComputedAction.
234 if os.path.basename(path) not in KNOWN_COMPUTED_USERS: 243 if os.path.basename(path) not in KNOWN_COMPUTED_USERS:
235 print >>sys.stderr, 'WARNING: {0} has RecordComputedAction at {1}'.\ 244 print >>sys.stderr, ('WARNING: %s has RecordComputedAction at %d' %
236 format(path, line_number) 245 (path, line_number))
237 246
238 def WalkDirectory(root_path, actions): 247 class WebUIActionsParser(HTMLParser):
248 """Parses an HTML file, looking for all tags with a 'metric' attribute.
249 Adds user actions corresponding to any metrics found.
250
251 Arguments:
252 actions: set of actions to add to
253 """
254 def __init__(self, actions):
255 HTMLParser.__init__(self)
256 self.actions = actions
257
258 def handle_starttag(self, tag, attrs):
259 # We only care to examine tags that have a 'metric' attribute.
260 attrs = dict(attrs)
261 if not 'metric' in attrs:
262 return
263
264 # Boolean metrics have two corresponding actions. All other metrics have
265 # just one corresponding action. By default, we check the 'dataType'
266 # attribute.
267 is_boolean = ('dataType' in attrs and attrs['dataType'] == 'boolean')
268 if 'type' in attrs and attrs['type'] in ('checkbox', 'radio'):
269 if attrs['type'] == 'checkbox':
270 # Checkboxes are boolean by default. However, their 'value-type' can
271 # instead be set to 'integer'.
272 if 'value-type' not in attrs or attrs['value-type'] in ['', 'boolean']:
273 is_boolean = True
274 else:
275 # Radio buttons are boolean if and only if their values are 'true' or
276 # 'false'.
277 assert(attrs['type'] == 'radio')
278 if 'value' in attrs and attrs['value'] in ['true', 'false']:
279 is_boolean = True
Ilya Sherman 2011/07/07 21:01:01 Actually, James, could you look at this bit of log
James Hawkins 2011/07/07 21:10:08 This part looks OK to me.
280
281 if is_boolean:
282 self.actions.add(attrs['metric'] + '_Enable')
283 self.actions.add(attrs['metric'] + '_Disable')
284 else:
285 self.actions.add(attrs['metric'])
286
287 def GrepForWebUIActions(path, actions):
288 """Grep a WebUI source file for elements with associated metrics.
289
290 Arguments:
291 path: path to the file
292 actions: set of actions to add to
293 """
294 parser = WebUIActionsParser(actions)
295 parser.feed(open(path).read())
296 parser.close()
297
298 def WalkDirectory(root_path, actions, extensions, callback):
239 for path, dirs, files in os.walk(root_path): 299 for path, dirs, files in os.walk(root_path):
240 if '.svn' in dirs: 300 if '.svn' in dirs:
241 dirs.remove('.svn') 301 dirs.remove('.svn')
242 for file in files: 302 for file in files:
243 ext = os.path.splitext(file)[1] 303 ext = os.path.splitext(file)[1]
244 if ext in ('.cc', '.mm', '.c', '.m'): 304 if ext in extensions:
245 GrepForActions(os.path.join(path, file), actions) 305 callback(os.path.join(path, file), actions)
306
307 def AddLiteralActions(actions):
308 """Add literal actions specified via calls to UserMetrics functions.
309
310 Arguments:
311 actions: set of actions to add to.
312 """
313 EXTENSIONS = ('.cc', '.mm', '.c', '.m')
314
315 # Walk the source tree to process all .cc files.
316 chrome_root = os.path.join(path_utils.ScriptDir(), '..')
317 WalkDirectory(chrome_root, actions, EXTENSIONS, GrepForActions)
318 content_root = os.path.join(path_utils.ScriptDir(), '..', '..', 'content')
319 WalkDirectory(content_root, actions, EXTENSIONS, GrepForActions)
320 webkit_root = os.path.join(path_utils.ScriptDir(), '..', '..', 'webkit')
321 WalkDirectory(os.path.join(webkit_root, 'glue'), actions, EXTENSIONS,
322 GrepForActions)
323 WalkDirectory(os.path.join(webkit_root, 'port'), actions, EXTENSIONS,
324 GrepForActions)
325
326 def AddWebUIActions(actions):
327 """Add user actions defined in WebUI files.
328
329 Arguments:
330 actions: set of actions to add to.
331 """
332 resources_root = os.path.join(path_utils.ScriptDir(), '..', 'browser',
333 'resources')
334 WalkDirectory(resources_root, actions, ('.html'), GrepForWebUIActions)
246 335
247 def main(argv): 336 def main(argv):
248 if '--hash' in argv: 337 if '--hash' in argv:
249 hash_output = True 338 hash_output = True
250 else: 339 else:
251 hash_output = False 340 hash_output = False
252 print >>sys.stderr, "WARNING: If you added new UMA tags, you must" + \ 341 print >>sys.stderr, "WARNING: If you added new UMA tags, you must" + \
253 " use the --hash option to update chromeactions.txt." 342 " use the --hash option to update chromeactions.txt."
254 # if we do a hash output, we want to only append NEW actions, and we know 343 # if we do a hash output, we want to only append NEW actions, and we know
255 # the file we want to work on 344 # the file we want to work on
256 actions = set() 345 actions = set()
257 346
258 chromeactions_path = os.path.join(path_utils.ScriptDir(), "chromeactions.txt") 347 chromeactions_path = os.path.join(path_utils.ScriptDir(), "chromeactions.txt")
259 348
260 if hash_output: 349 if hash_output:
261 f = open(chromeactions_path) 350 f = open(chromeactions_path)
262 for line in f: 351 for line in f:
263 part = line.rpartition("\t") 352 part = line.rpartition("\t")
264 part = part[2].strip() 353 part = part[2].strip()
265 actions.add(part) 354 actions.add(part)
266 f.close() 355 f.close()
267 356
268 357
269 AddComputedActions(actions) 358 AddComputedActions(actions)
270 # TODO(fmantek): bring back webkit editor actions. 359 # TODO(fmantek): bring back webkit editor actions.
271 # AddWebKitEditorActions(actions) 360 # AddWebKitEditorActions(actions)
272 AddAboutFlagsActions(actions) 361 AddAboutFlagsActions(actions)
362 AddWebUIActions(actions)
273 363
274 # Walk the source tree to process all .cc files. 364 AddLiteralActions(actions)
275 chrome_root = os.path.join(path_utils.ScriptDir(), '..')
276 WalkDirectory(chrome_root, actions)
277 content_root = os.path.join(path_utils.ScriptDir(), '..', '..', 'content')
278 WalkDirectory(content_root, actions)
279 webkit_root = os.path.join(path_utils.ScriptDir(), '..', '..', 'webkit')
280 WalkDirectory(os.path.join(webkit_root, 'glue'), actions)
281 WalkDirectory(os.path.join(webkit_root, 'port'), actions)
282 365
283 # print "Scanned {0} number of files".format(number_of_files_total) 366 # print "Scanned {0} number of files".format(number_of_files_total)
284 # print "Found {0} entries".format(len(actions)) 367 # print "Found {0} entries".format(len(actions))
285 368
286 AddClosedSourceActions(actions) 369 AddClosedSourceActions(actions)
287 AddChromeOSActions(actions) 370 AddChromeOSActions(actions)
288 371
289 if hash_output: 372 if hash_output:
290 f = open(chromeactions_path, "w") 373 f = open(chromeactions_path, "w")
291 374
292 375
293 # Print out the actions as a sorted list. 376 # Print out the actions as a sorted list.
294 for action in sorted(actions): 377 for action in sorted(actions):
295 if hash_output: 378 if hash_output:
296 hash = hashlib.md5() 379 hash = hashlib.md5()
297 hash.update(action) 380 hash.update(action)
298 print >>f, '0x%s\t%s' % (hash.hexdigest()[:16], action) 381 print >>f, '0x%s\t%s' % (hash.hexdigest()[:16], action)
299 else: 382 else:
300 print action 383 print action
301 384
302 if hash_output: 385 if hash_output:
303 print "Done. Do not forget to add chromeactions.txt to your changelist" 386 print "Done. Do not forget to add chromeactions.txt to your changelist"
304 387
305 if '__main__' == __name__: 388 if '__main__' == __name__:
306 main(sys.argv) 389 main(sys.argv)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698