1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 """Handles generating profiles and transferring them to/from mobile devices."""
15 from telemetry import benchmark
16 from telemetry.core import browser_options
17 from telemetry.core import discover
18 from telemetry.core import util
19 from telemetry.page import page_runner
20 from telemetry.page import profile_creator
21 from telemetry.page import test_expectations
22 from telemetry.results import results_options
25 def _DiscoverProfileCreatorClasses():
26 profile_creators_dir = os.path.abspath(os.path.join(util.GetBaseDir(),
27 os.pardir, 'perf', 'profile_creators'))
28 base_dir = os.path.abspath(os.path.join(profile_creators_dir, os.pardir))
30 profile_creators_unfiltered = discover.DiscoverClasses(
31 profile_creators_dir, base_dir, profile_creator.ProfileCreator)
33 # Remove '_creator' suffix from keys.
35 for test_name, test_class in profile_creators_unfiltered.iteritems():
36 assert test_name.endswith('_creator')
37 test_name = test_name[:-len('_creator')]
38 profile_creators[test_name] = test_class
39 return profile_creators
42 def _IsPseudoFile(directory, paths):
43 """Filter function for shutil.copytree() to reject socket files and symlinks
44 since those can't be copied around on bots."""
45 def IsSocket(full_path):
46 """Check if a file at a given path is a socket."""
48 if stat.S_ISSOCK(os.stat(full_path).st_mode):
51 # Thrown if we encounter a broken symlink.
57 full_path = os.path.join(directory, path)
59 if os.path.isdir(full_path):
61 if not IsSocket(full_path) and not os.path.islink(full_path):
64 logging.warning('Ignoring pseudo file: %s' % full_path)
65 ignore_list.append(path)
69 def GenerateProfiles(profile_creator_class, profile_creator_name, options):
70 """Generate a profile"""
71 expectations = test_expectations.TestExpectations()
72 test = profile_creator_class()
74 temp_output_directory = tempfile.mkdtemp()
75 options.output_profile_path = temp_output_directory
77 results = results_options.CreateResults(
78 benchmark.BenchmarkMetadata(test.__class__.__name__), options)
79 page_runner.Run(test, test.page_set, expectations, options, results)
82 logging.warning('Some pages failed.')
83 logging.warning('Failed pages:\n%s',
84 '\n'.join(map(str, results.pages_that_failed)))
87 # Everything is a-ok, move results to final destination.
88 generated_profiles_dir = os.path.abspath(options.output_dir)
89 if not os.path.exists(generated_profiles_dir):
90 os.makedirs(generated_profiles_dir)
91 out_path = os.path.join(generated_profiles_dir, profile_creator_name)
92 if os.path.exists(out_path):
93 shutil.rmtree(out_path)
95 shutil.copytree(temp_output_directory, out_path, ignore=_IsPseudoFile)
96 shutil.rmtree(temp_output_directory)
97 sys.stderr.write("SUCCESS: Generated profile copied to: '%s'.\n" % out_path)
102 def AddCommandLineArgs(parser):
103 page_runner.AddCommandLineArgs(parser)
105 profile_creators = _DiscoverProfileCreatorClasses().keys()
106 legal_profile_creators = '|'.join(profile_creators)
107 group = optparse.OptionGroup(parser, 'Profile generation options')
108 group.add_option('--profile-type-to-generate',
109 dest='profile_type_to_generate',
111 help='Type of profile to generate. '
112 'Supported values: %s' % legal_profile_creators)
113 parser.add_option_group(group)
116 def ProcessCommandLineArgs(parser, args):
117 page_runner.ProcessCommandLineArgs(parser, args)
119 if not args.profile_type_to_generate:
120 parser.error("Must specify --profile-type-to-generate option.")
122 profile_creators = _DiscoverProfileCreatorClasses().keys()
123 if args.profile_type_to_generate not in profile_creators:
124 legal_profile_creators = '|'.join(profile_creators)
125 parser.error("Invalid profile type, legal values are: %s." %
126 legal_profile_creators)
128 if not args.browser_type:
129 parser.error("Must specify --browser option.")
131 if not args.output_dir:
132 parser.error("Must specify --output-dir option.")
134 if args.browser_options.dont_override_profile:
135 parser.error("Can't use existing profile when generating profile.")
139 options = browser_options.BrowserFinderOptions()
140 parser = options.CreateParser(
141 "%%prog <--profile-type-to-generate=...> <--browser=...> <--output-dir>")
142 AddCommandLineArgs(parser)
143 _, _ = parser.parse_args()
144 ProcessCommandLineArgs(parser, options)
147 profile_creators = _DiscoverProfileCreatorClasses()
148 profile_creator_class = profile_creators[options.profile_type_to_generate]
149 return GenerateProfiles(profile_creator_class,
150 options.profile_type_to_generate, options)