[go: nahoru, domu]

blob: 3a1375846bbbd032727045ff0a6ee60d442e4809 [file] [log] [blame]
Egor Pasko0462e852d2018-03-29 15:52:091#!/usr/bin/env vpython
Benoit Lizea3fe2932017-10-20 10:24:522# Copyright (c) 2013 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6""" A utility to generate an up-to-date orderfile.
7
8The orderfile is used by the linker to order text sections such that the
9sections are placed consecutively in the order specified. This allows us
10to page in less code during start-up.
11
12Example usage:
Egor Pasko93e514e2017-10-31 13:32:3613 tools/cygprofile/orderfile_generator_backend.py -l 20 -j 1000 --use-goma \
Benoit Lizea3fe2932017-10-20 10:24:5214 --target-arch=arm
15"""
16
Benoit Lizea1b64f82017-12-07 10:12:5017import argparse
Benoit Lizea3fe2932017-10-20 10:24:5218import hashlib
19import json
Matthew Cary69e9e422018-08-10 18:30:0620import glob
Benoit Lizea3fe2932017-10-20 10:24:5221import logging
Benoit Lizea3fe2932017-10-20 10:24:5222import os
23import re
24import shutil
25import subprocess
26import sys
Benoit Lizea87e5bc2017-11-07 15:12:5727import tempfile
Benoit Lizea3fe2932017-10-20 10:24:5228import time
29
Matthew Cary91df9792018-11-30 14:35:1530import cluster
Matthew Carye8400642018-06-14 15:43:0231import cyglog_to_orderfile
Benoit Lizea3fe2932017-10-20 10:24:5232import cygprofile_utils
Benoit Lizefefbb27c2018-01-17 13:54:1833import patch_orderfile
Benoit Lizea87e5bc2017-11-07 15:12:5734import process_profiles
Egor Pasko3bc0b932018-04-03 10:08:2735import profile_android_startup
Benoit Lizefefbb27c2018-01-17 13:54:1836import symbol_extractor
Benoit Lizea3fe2932017-10-20 10:24:5237
Monica Salamaeea2d942019-03-11 12:36:1838_SRC_PATH = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)
39sys.path.append(os.path.join(_SRC_PATH, 'third_party', 'catapult', 'devil'))
40from devil.android import device_utils
41from devil.android.sdk import version_codes
42
Benoit Lizea3fe2932017-10-20 10:24:5243
44_SRC_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)),
45 os.pardir, os.pardir)
46sys.path.append(os.path.join(_SRC_PATH, 'build', 'android'))
47import devil_chromium
48from pylib import constants
49
50
51# Needs to happen early for GetBuildType()/GetOutDirectory() to work correctly
52constants.SetBuildType('Release')
53
54
Matthew Cary53f74ba2019-01-24 10:07:1855# Architecture specific GN args. Trying to build an orderfile for an
56# architecture not listed here will eventually throw.
57_ARCH_GN_ARGS = {
58 'arm': [ 'target_cpu = "arm"' ],
59 'arm64': [ 'target_cpu = "arm64"',
60 'android_64bit_browser = true'],
61}
62
Benoit Lizea3fe2932017-10-20 10:24:5263class CommandError(Exception):
64 """Indicates that a dispatched shell command exited with a non-zero status."""
65
66 def __init__(self, value):
67 super(CommandError, self).__init__()
68 self.value = value
69
70 def __str__(self):
71 return repr(self.value)
72
73
74def _GenerateHash(file_path):
75 """Calculates and returns the hash of the file at file_path."""
76 sha1 = hashlib.sha1()
77 with open(file_path, 'rb') as f:
78 while True:
79 # Read in 1mb chunks, so it doesn't all have to be loaded into memory.
80 chunk = f.read(1024 * 1024)
81 if not chunk:
82 break
83 sha1.update(chunk)
84 return sha1.hexdigest()
85
86
87def _GetFileExtension(file_name):
88 """Calculates the file extension from a file name.
89
90 Args:
91 file_name: The source file name.
92 Returns:
93 The part of file_name after the dot (.) or None if the file has no
94 extension.
95 Examples: /home/user/foo.bar -> bar
96 /home/user.name/foo -> None
97 /home/user/.foo -> None
98 /home/user/foo.bar.baz -> baz
99 """
100 file_name_parts = os.path.basename(file_name).split('.')
101 if len(file_name_parts) > 1:
102 return file_name_parts[-1]
103 else:
104 return None
105
106
107def _StashOutputDirectory(buildpath):
108 """Takes the output directory and stashes it in the default output directory.
109
110 This allows it to be used for incremental builds next time (after unstashing)
111 by keeping it in a place that isn't deleted normally, while also ensuring
112 that it is properly clobbered when appropriate.
113
114 This is a dirty hack to deal with the needs of clobbering while also handling
115 incremental builds and the hardcoded relative paths used in some of the
116 project files.
117
118 Args:
119 buildpath: The path where the building happens. If this corresponds to the
120 default output directory, no action is taken.
121 """
122 if os.path.abspath(buildpath) == os.path.abspath(os.path.dirname(
123 constants.GetOutDirectory())):
124 return
125 name = os.path.basename(buildpath)
126 stashpath = os.path.join(constants.GetOutDirectory(), name)
127 if not os.path.exists(buildpath):
128 return
129 if os.path.exists(stashpath):
130 shutil.rmtree(stashpath, ignore_errors=True)
131 shutil.move(buildpath, stashpath)
132
133
134def _UnstashOutputDirectory(buildpath):
135 """Inverse of _StashOutputDirectory.
136
137 Moves the output directory stashed within the default output directory
138 (out/Release) to the position where the builds can actually happen.
139
140 This is a dirty hack to deal with the needs of clobbering while also handling
141 incremental builds and the hardcoded relative paths used in some of the
142 project files.
143
144 Args:
145 buildpath: The path where the building happens. If this corresponds to the
146 default output directory, no action is taken.
147 """
148 if os.path.abspath(buildpath) == os.path.abspath(os.path.dirname(
149 constants.GetOutDirectory())):
150 return
151 name = os.path.basename(buildpath)
152 stashpath = os.path.join(constants.GetOutDirectory(), name)
153 if not os.path.exists(stashpath):
154 return
155 if os.path.exists(buildpath):
156 shutil.rmtree(buildpath, ignore_errors=True)
157 shutil.move(stashpath, buildpath)
158
159
160class StepRecorder(object):
161 """Records steps and timings."""
162
163 def __init__(self, buildbot):
164 self.timings = []
165 self._previous_step = ('', 0.0)
166 self._buildbot = buildbot
167 self._error_recorded = False
168
169 def BeginStep(self, name):
170 """Marks a beginning of the next step in the script.
171
172 On buildbot, this prints a specially formatted name that will show up
173 in the waterfall. Otherwise, just prints the step name.
174
175 Args:
176 name: The name of the step.
177 """
178 self.EndStep()
179 self._previous_step = (name, time.time())
180 print 'Running step: ', name
181
182 def EndStep(self):
183 """Records successful completion of the current step.
184
185 This is optional if the step is immediately followed by another BeginStep.
186 """
187 if self._previous_step[0]:
188 elapsed = time.time() - self._previous_step[1]
189 print 'Step %s took %f seconds' % (self._previous_step[0], elapsed)
190 self.timings.append((self._previous_step[0], elapsed))
191
192 self._previous_step = ('', 0.0)
193
194 def FailStep(self, message=None):
195 """Marks that a particular step has failed.
196
197 On buildbot, this will mark the current step as failed on the waterfall.
198 Otherwise we will just print an optional failure message.
199
200 Args:
201 message: An optional explanation as to why the step failed.
202 """
203 print 'STEP FAILED!!'
204 if message:
205 print message
206 self._error_recorded = True
207 self.EndStep()
208
209 def ErrorRecorded(self):
210 """True if FailStep has been called."""
211 return self._error_recorded
212
213 def RunCommand(self, cmd, cwd=constants.DIR_SOURCE_ROOT, raise_on_error=True,
214 stdout=None):
215 """Execute a shell command.
216
217 Args:
218 cmd: A list of command strings.
Matthew Cary78aae162018-08-10 17:16:30219 cwd: Directory in which the command should be executed, defaults to build
220 root of script's location if not specified.
Benoit Lizea3fe2932017-10-20 10:24:52221 raise_on_error: If true will raise a CommandError if the call doesn't
222 succeed and mark the step as failed.
223 stdout: A file to redirect stdout for the command to.
224
225 Returns:
226 The process's return code.
227
228 Raises:
229 CommandError: An error executing the specified command.
230 """
231 print 'Executing %s in %s' % (' '.join(cmd), cwd)
232 process = subprocess.Popen(cmd, stdout=stdout, cwd=cwd, env=os.environ)
233 process.wait()
234 if raise_on_error and process.returncode != 0:
235 self.FailStep()
236 raise CommandError('Exception executing command %s' % ' '.join(cmd))
237 return process.returncode
238
239
240class ClankCompiler(object):
241 """Handles compilation of clank."""
242
243 def __init__(self, out_dir, step_recorder, arch, jobs, max_load, use_goma,
Stephen Kylef11339f2019-03-25 09:00:47244 goma_dir, system_health_profiling, monochrome, public,
245 orderfile_location):
Benoit Lizea3fe2932017-10-20 10:24:52246 self._out_dir = out_dir
247 self._step_recorder = step_recorder
Benoit Lizea87e5bc2017-11-07 15:12:57248 self._arch = arch
249 self._jobs = jobs
250 self._max_load = max_load
Benoit Lizea3fe2932017-10-20 10:24:52251 self._use_goma = use_goma
Benoit Lizea87e5bc2017-11-07 15:12:57252 self._goma_dir = goma_dir
Matthew Cary78aae162018-08-10 17:16:30253 self._system_health_profiling = system_health_profiling
Stephen Kylef11339f2019-03-25 09:00:47254 self._public = public
255 self._orderfile_location = orderfile_location
Matthew Caryd6bfcb72018-09-14 11:27:46256 if monochrome:
257 self._apk = 'Monochrome.apk'
Matthew Carye1b00062018-09-19 14:27:44258 self._apk_target = 'monochrome_apk'
Matthew Caryd6bfcb72018-09-14 11:27:46259 self._libname = 'libmonochrome'
260 self._libchrome_target = 'monochrome'
261 else:
262 self._apk = 'Chrome.apk'
Matthew Carye1b00062018-09-19 14:27:44263 self._apk_target = 'chrome_apk'
Matthew Caryd6bfcb72018-09-14 11:27:46264 self._libname = 'libchrome'
265 self._libchrome_target = 'libchrome'
Stephen Kylef11339f2019-03-25 09:00:47266 if public:
267 self._apk = self._apk.replace('.apk', 'Public.apk')
268 self._apk_target = self._apk_target.replace('_apk', '_public_apk')
Matthew Cary78aae162018-08-10 17:16:30269
270 self.obj_dir = os.path.join(self._out_dir, 'Release', 'obj')
Benoit Lizea3fe2932017-10-20 10:24:52271 self.lib_chrome_so = os.path.join(
Matthew Caryd6bfcb72018-09-14 11:27:46272 self._out_dir, 'Release', 'lib.unstripped',
273 '{}.so'.format(self._libname))
274 self.chrome_apk = os.path.join(self._out_dir, 'Release', 'apks', self._apk)
Benoit Lizea3fe2932017-10-20 10:24:52275
276 def Build(self, instrumented, target):
277 """Builds the provided ninja target with or without order_profiling on.
278
279 Args:
Benoit Lizea87e5bc2017-11-07 15:12:57280 instrumented: (bool) Whether we want to build an instrumented binary.
281 target: (str) The name of the ninja target to build.
Benoit Lizea3fe2932017-10-20 10:24:52282 """
283 self._step_recorder.BeginStep('Compile %s' % target)
284
285 # Set the "Release Official" flavor, the parts affecting performance.
286 args = [
Peter Collingbourne1c85d5d22018-08-22 18:06:35287 'enable_resource_whitelist_generation=false',
Stephen Kylef11339f2019-03-25 09:00:47288 'is_chrome_branded=' + str(not self._public).lower(),
Benoit Lizea3fe2932017-10-20 10:24:52289 'is_debug=false',
290 'is_official_build=true',
Benoit Lizea3fe2932017-10-20 10:24:52291 'target_os="android"',
292 'use_goma=' + str(self._use_goma).lower(),
293 'use_order_profiling=' + str(instrumented).lower(),
294 ]
Matthew Cary53f74ba2019-01-24 10:07:18295 args += _ARCH_GN_ARGS[self._arch]
Benoit Lizea3fe2932017-10-20 10:24:52296 if self._goma_dir:
297 args += ['goma_dir="%s"' % self._goma_dir]
Matthew Cary78aae162018-08-10 17:16:30298 if self._system_health_profiling:
299 args += ['devtools_instrumentation_dumping = ' +
300 str(instrumented).lower()]
Benoit Lizea87e5bc2017-11-07 15:12:57301
Stephen Kylef11339f2019-03-25 09:00:47302 if self._public and os.path.exists(self._orderfile_location):
303 # GN needs the orderfile path to be source-absolute.
304 src_abs_orderfile = os.path.relpath(self._orderfile_location,
305 constants.DIR_SOURCE_ROOT)
306 args += ['chrome_orderfile="//{}"'.format(src_abs_orderfile)]
307
Benoit Lizea3fe2932017-10-20 10:24:52308 self._step_recorder.RunCommand(
309 ['gn', 'gen', os.path.join(self._out_dir, 'Release'),
310 '--args=' + ' '.join(args)])
311
312 self._step_recorder.RunCommand(
313 ['ninja', '-C', os.path.join(self._out_dir, 'Release'),
314 '-j' + str(self._jobs), '-l' + str(self._max_load), target])
315
316 def CompileChromeApk(self, instrumented, force_relink=False):
317 """Builds a Chrome.apk either with or without order_profiling on.
318
319 Args:
Benoit Lizea87e5bc2017-11-07 15:12:57320 instrumented: (bool) Whether to build an instrumented apk.
Benoit Lizea3fe2932017-10-20 10:24:52321 force_relink: Whether libchromeview.so should be re-created.
322 """
323 if force_relink:
324 self._step_recorder.RunCommand(['rm', '-rf', self.lib_chrome_so])
Matthew Carye1b00062018-09-19 14:27:44325 self.Build(instrumented, self._apk_target)
Benoit Lizea3fe2932017-10-20 10:24:52326
327 def CompileLibchrome(self, instrumented, force_relink=False):
328 """Builds a libchrome.so either with or without order_profiling on.
329
330 Args:
Benoit Lizea87e5bc2017-11-07 15:12:57331 instrumented: (bool) Whether to build an instrumented apk.
332 force_relink: (bool) Whether libchrome.so should be re-created.
Benoit Lizea3fe2932017-10-20 10:24:52333 """
334 if force_relink:
335 self._step_recorder.RunCommand(['rm', '-rf', self.lib_chrome_so])
Matthew Caryd6bfcb72018-09-14 11:27:46336 self.Build(instrumented, self._libchrome_target)
Benoit Lizea3fe2932017-10-20 10:24:52337
338
339class OrderfileUpdater(object):
340 """Handles uploading and committing a new orderfile in the repository.
341
342 Only used for testing or on a bot.
343 """
344
345 _CLOUD_STORAGE_BUCKET_FOR_DEBUG = None
346 _CLOUD_STORAGE_BUCKET = None
347 _UPLOAD_TO_CLOUD_COMMAND = 'upload_to_google_storage.py'
348
349 def __init__(self, repository_root, step_recorder, branch, netrc):
350 """Constructor.
351
352 Args:
353 repository_root: (str) Root of the target repository.
354 step_recorder: (StepRecorder) Step recorder, for logging.
355 branch: (str) Branch to commit to.
356 netrc: (str) Path to the .netrc file to use.
357 """
358 self._repository_root = repository_root
359 self._step_recorder = step_recorder
360 self._branch = branch
361 self._netrc = netrc
362
Matthew Cary86a226e2019-03-19 12:17:44363 def CommitStashedFileHashes(self, files):
364 """Commits unpatched and patched orderfiles hashes if changed.
365
366 The files are committed only if their associated sha1 hash files match, and
367 are modified in git. In normal operations the hash files are changed only
368 when a file is uploaded to cloud storage. If the hash file is not modified
369 in git, the file is skipped.
370
371 Args:
372 files: [str or None] specifies file paths. None items are ignored.
373
374 Raises:
375 Exception if the hash file does not match the file.
376 NotImplementedError when the commit logic hasn't been overridden.
377 """
378 files_to_commit = list(filter(None, files))
379 if files_to_commit:
380 self._CommitStashedFiles(files_to_commit)
381
382 def LegacyCommitFileHashes(self,
383 unpatched_orderfile_filename,
384 orderfile_filename):
Benoit Lizea3fe2932017-10-20 10:24:52385 """Commits unpatched and patched orderfiles hashes, if provided.
386
Matthew Cary86a226e2019-03-19 12:17:44387 DEPRECATED. Left in place during transition.
388
Benoit Lizea3fe2932017-10-20 10:24:52389 Files must have been successfilly uploaded to cloud storage first.
390
391 Args:
392 unpatched_orderfile_filename: (str or None) Unpatched orderfile path.
393 orderfile_filename: (str or None) Orderfile path.
394
395 Raises:
Matthew Cary86a226e2019-03-19 12:17:44396 NotImplementedError when the commit logic hasn't been overridden.
Benoit Lizea3fe2932017-10-20 10:24:52397 """
398 files_to_commit = []
399 commit_message_lines = ['Update Orderfile.']
400 for filename in [unpatched_orderfile_filename, orderfile_filename]:
401 if not filename:
402 continue
403 (relative_path, sha1) = self._GetHashFilePathAndContents(filename)
404 commit_message_lines.append('Profile: %s: %s' % (
405 os.path.basename(relative_path), sha1))
406 files_to_commit.append(relative_path)
407 if files_to_commit:
408 self._CommitFiles(files_to_commit, commit_message_lines)
409
410 def UploadToCloudStorage(self, filename, use_debug_location):
411 """Uploads a file to cloud storage.
412
413 Args:
414 filename: (str) File to upload.
415 use_debug_location: (bool) Whether to use the debug location.
416 """
417 bucket = (self._CLOUD_STORAGE_BUCKET_FOR_DEBUG if use_debug_location
418 else self._CLOUD_STORAGE_BUCKET)
419 extension = _GetFileExtension(filename)
420 cmd = [self._UPLOAD_TO_CLOUD_COMMAND, '--bucket', bucket]
421 if extension:
422 cmd.extend(['-z', extension])
423 cmd.append(filename)
424 self._step_recorder.RunCommand(cmd)
425 print 'Download: https://sandbox.google.com/storage/%s/%s' % (
426 bucket, _GenerateHash(filename))
427
428 def _GetHashFilePathAndContents(self, filename):
429 """Gets the name and content of the hash file created from uploading the
430 given file.
431
432 Args:
433 filename: (str) The file that was uploaded to cloud storage.
434
435 Returns:
436 A tuple of the hash file name, relative to the reository root, and the
437 content, which should be the sha1 hash of the file
438 ('base_file.sha1', hash)
439 """
440 abs_hash_filename = filename + '.sha1'
441 rel_hash_filename = os.path.relpath(
442 abs_hash_filename, self._repository_root)
443 with open(abs_hash_filename, 'r') as f:
444 return (rel_hash_filename, f.read())
445
446 def _CommitFiles(self, files_to_commit, commit_message_lines):
447 """Commits a list of files, with a given message."""
448 raise NotImplementedError
449
Matthew Cary86a226e2019-03-19 12:17:44450 def _GitStash(self):
451 """Git stash the current clank tree.
452
453 Raises:
454 NotImplementedError when the stash logic hasn't been overridden.
455 """
456 raise NotImplementedError
457
458 def _CommitStashedFiles(self, expected_files_in_stash):
459 """Commits stashed files.
460
461 The local repository is updated and then the files to commit are taken from
462 modified files from the git stash. The modified files should be a subset of
463 |expected_files_in_stash|. If there are unexpected modified files, this
464 function may raise. This is meant to be paired with _GitStash().
465
466 Args:
467 expected_files_in_stash: [str] paths to a possible superset of files
468 expected to be stashed & committed.
469
470 Raises:
471 NotImplementedError when the commit logic hasn't been overridden.
472 """
473 raise NotImplementedError
474
Benoit Lizea3fe2932017-10-20 10:24:52475
Stephen Kylef11339f2019-03-25 09:00:47476class OrderfileNoopUpdater(OrderfileUpdater):
477 def CommitFileHashes(self, unpatched_orderfile_filename, orderfile_filename):
478 # pylint: disable=unused-argument
479 return
480
481 def UploadToCloudStorage(self, filename, use_debug_location):
482 # pylint: disable=unused-argument
483 return
484
485 def _CommitFiles(self, files_to_commit, commit_message_lines):
486 raise NotImplementedError
487
488
Benoit Lizea3fe2932017-10-20 10:24:52489class OrderfileGenerator(object):
490 """A utility for generating a new orderfile for Clank.
491
492 Builds an instrumented binary, profiles a run of the application, and
493 generates an updated orderfile.
494 """
Benoit Lizea3fe2932017-10-20 10:24:52495 _CHECK_ORDERFILE_SCRIPT = os.path.join(
496 constants.DIR_SOURCE_ROOT, 'tools', 'cygprofile', 'check_orderfile.py')
497 _BUILD_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(
498 constants.GetOutDirectory()))) # Normally /path/to/src
499
Benoit Lizea3fe2932017-10-20 10:24:52500 # Previous orderfile_generator debug files would be overwritten.
501 _DIRECTORY_FOR_DEBUG_FILES = '/tmp/orderfile_generator_debug_files'
502
Stephen Kylef11339f2019-03-25 09:00:47503 def _PrepareOrderfilePaths(self):
504 if self._options.public:
505 self._clank_dir = os.path.join(constants.DIR_SOURCE_ROOT,
506 '')
507 if not os.path.exists(os.path.join(self._clank_dir, 'orderfiles')):
508 os.makedirs(os.path.join(self._clank_dir, 'orderfiles'))
509 else:
510 self._clank_dir = os.path.join(constants.DIR_SOURCE_ROOT,
511 'clank')
512
513 self._unpatched_orderfile_filename = os.path.join(
514 self._clank_dir, 'orderfiles', 'unpatched_orderfile.%s')
515 self._path_to_orderfile = os.path.join(
516 self._clank_dir, 'orderfiles', 'orderfile.%s.out')
517
Benoit Lizea3fe2932017-10-20 10:24:52518 def _GetPathToOrderfile(self):
519 """Gets the path to the architecture-specific orderfile."""
Stephen Kylef11339f2019-03-25 09:00:47520 return self._path_to_orderfile % self._options.arch
Benoit Lizea3fe2932017-10-20 10:24:52521
522 def _GetUnpatchedOrderfileFilename(self):
523 """Gets the path to the architecture-specific unpatched orderfile."""
Stephen Kylef11339f2019-03-25 09:00:47524 return self._unpatched_orderfile_filename % self._options.arch
Benoit Lizea3fe2932017-10-20 10:24:52525
Monica Salamaeea2d942019-03-11 12:36:18526 def _SetDevice(self):
527 """ Selects the device to be used by the script.
528
529 Returns:
530 (Device with given serial ID) : if the --device flag is set.
531 (Device running Android[K,L]) : if --use-legacy-chrome-apk flag is set or
532 no device running Android N+ was found.
533 (Device running Android N+) : Otherwise.
534
535 Raises Error:
536 If no device meeting the requirements has been found.
537 """
538 devices = None
539 if self._options.device:
540 devices = [device_utils.DeviceUtils(self._options.device)]
541 else:
542 devices = device_utils.DeviceUtils.HealthyDevices()
543
544 assert devices, 'Expected at least one connected device'
545
546 if self._options.use_legacy_chrome_apk:
547 self._monochrome = False
548 for device in devices:
549 device_version = device.build_version_sdk
550 if (device_version >= version_codes.KITKAT
551 and device_version <= version_codes.LOLLIPOP_MR1):
552 return device
553
554 assert not self._options.use_legacy_chrome_apk, \
555 'No device found running suitable android version for Chrome.apk.'
556
557 preferred_device = None
558 for device in devices:
559 if device.build_version_sdk >= version_codes.NOUGAT:
Monica Salama90b8f5b2019-04-25 11:10:38560 preferred_device = device
561 break
Monica Salamaeea2d942019-03-11 12:36:18562
563 self._monochrome = preferred_device is not None
564
565 return preferred_device if preferred_device else devices[0]
566
567
Benoit Lizea3fe2932017-10-20 10:24:52568 def __init__(self, options, orderfile_updater_class):
569 self._options = options
570
571 self._instrumented_out_dir = os.path.join(
572 self._BUILD_ROOT, self._options.arch + '_instrumented_out')
573 self._uninstrumented_out_dir = os.path.join(
574 self._BUILD_ROOT, self._options.arch + '_uninstrumented_out')
Monica Salama90b8f5b2019-04-25 11:10:38575 self._no_orderfile_out_dir = os.path.join(
576 self._BUILD_ROOT, self._options.arch + '_no_orderfile_out')
Benoit Lizea3fe2932017-10-20 10:24:52577
Stephen Kylef11339f2019-03-25 09:00:47578 self._PrepareOrderfilePaths()
579
Benoit Lizea3fe2932017-10-20 10:24:52580 if options.profile:
Benoit Lizea1b64f82017-12-07 10:12:50581 output_directory = os.path.join(self._instrumented_out_dir, 'Release')
Matthew Caryb8daed942018-06-11 10:58:08582 host_profile_dir = os.path.join(output_directory, 'profile_data')
Benoit Lizea1b64f82017-12-07 10:12:50583 urls = [profile_android_startup.AndroidProfileTool.TEST_URL]
584 use_wpr = True
585 simulate_user = False
Benoit L96466812018-03-06 15:58:37586 urls = options.urls
587 use_wpr = not options.no_wpr
588 simulate_user = options.simulate_user
Monica Salamaeea2d942019-03-11 12:36:18589 device = self._SetDevice()
Benoit Lizea3fe2932017-10-20 10:24:52590 self._profiler = profile_android_startup.AndroidProfileTool(
Matthew Cary453ff1452018-07-18 12:19:35591 output_directory, host_profile_dir, use_wpr, urls, simulate_user,
Matthew Cary1ea82212019-03-20 12:10:27592 device, debug=self._options.streamline_for_debugging)
Matthew Cary69e9e422018-08-10 18:30:06593 if options.pregenerated_profiles:
594 self._profiler.SetPregeneratedProfiles(
595 glob.glob(options.pregenerated_profiles))
596 else:
597 assert not options.pregenerated_profiles, (
598 '--pregenerated-profiles cannot be used with --skip-profile')
599 assert not options.profile_save_dir, (
600 '--profile-save-dir cannot be used with --skip-profile')
Monica Salamaeea2d942019-03-11 12:36:18601 self._monochrome = not self._options.use_legacy_chrome_apk
Benoit Lizea3fe2932017-10-20 10:24:52602
Matthew Caryf949bba2019-02-04 13:39:23603 # Outlined function handling enabled by default for all architectures.
604 self._order_outlined_functions = not options.noorder_outlined_functions
605
Benoit Lizea3fe2932017-10-20 10:24:52606 self._output_data = {}
607 self._step_recorder = StepRecorder(options.buildbot)
608 self._compiler = None
Stephen Kylef11339f2019-03-25 09:00:47609 if orderfile_updater_class is None:
610 if options.public:
611 orderfile_updater_class = OrderfileNoopUpdater
612 else:
613 orderfile_updater_class = OrderfileUpdater
Benoit Lizea3fe2932017-10-20 10:24:52614 assert issubclass(orderfile_updater_class, OrderfileUpdater)
Stephen Kylef11339f2019-03-25 09:00:47615 self._orderfile_updater = orderfile_updater_class(self._clank_dir,
Benoit Lizea3fe2932017-10-20 10:24:52616 self._step_recorder,
617 options.branch,
618 options.netrc)
619 assert os.path.isdir(constants.DIR_SOURCE_ROOT), 'No src directory found'
Benoit Lizefefbb27c2018-01-17 13:54:18620 symbol_extractor.SetArchitecture(options.arch)
Benoit Lizea3fe2932017-10-20 10:24:52621
Benoit Lizea3fe2932017-10-20 10:24:52622 @staticmethod
623 def _RemoveBlanks(src_file, dest_file):
624 """A utility to remove blank lines from a file.
625
626 Args:
627 src_file: The name of the file to remove the blanks from.
628 dest_file: The name of the file to write the output without blanks.
629 """
630 assert src_file != dest_file, 'Source and destination need to be distinct'
631
632 try:
633 src = open(src_file, 'r')
634 dest = open(dest_file, 'w')
635 for line in src:
636 if line and not line.isspace():
637 dest.write(line)
638 finally:
639 src.close()
640 dest.close()
641
642 def _GenerateAndProcessProfile(self):
Matthew Cary78aae162018-08-10 17:16:30643 """Invokes a script to merge the per-thread traces into one file.
644
645 The produced list of offsets is saved in
646 self._GetUnpatchedOrderfileFilename().
647 """
Benoit Lizea3fe2932017-10-20 10:24:52648 self._step_recorder.BeginStep('Generate Profile Data')
649 files = []
Matthew Cary78aae162018-08-10 17:16:30650 logging.getLogger().setLevel(logging.DEBUG)
651 if self._options.system_health_orderfile:
652 files = self._profiler.CollectSystemHealthProfile(
653 self._compiler.chrome_apk)
Matthew Cary69e9e422018-08-10 18:30:06654 self._MaybeSaveProfile(files)
Matthew Cary78aae162018-08-10 17:16:30655 try:
656 self._ProcessPhasedOrderfile(files)
657 except Exception:
658 for f in files:
659 self._SaveForDebugging(f)
Matthew Cary8b1416232018-08-10 19:12:22660 self._SaveForDebugging(self._compiler.lib_chrome_so)
Matthew Cary78aae162018-08-10 17:16:30661 raise
662 finally:
663 self._profiler.Cleanup()
664 else:
665 self._CollectLegacyProfile()
666 logging.getLogger().setLevel(logging.INFO)
667
668 def _ProcessPhasedOrderfile(self, files):
669 """Process the phased orderfiles produced by system health benchmarks.
670
671 The offsets will be placed in _GetUnpatchedOrderfileFilename().
672
673 Args:
674 file: Profile files pulled locally.
675 """
676 self._step_recorder.BeginStep('Process Phased Orderfile')
677 profiles = process_profiles.ProfileManager(files)
678 processor = process_profiles.SymbolOffsetProcessor(
679 self._compiler.lib_chrome_so)
Matthew Caryda9db932019-03-11 15:28:17680 ordered_symbols = cluster.ClusterOffsets(profiles, processor)
Matthew Cary8b1416232018-08-10 19:12:22681 if not ordered_symbols:
682 raise Exception('Failed to get ordered symbols')
Matthew Caryda9db932019-03-11 15:28:17683 for sym in ordered_symbols:
684 assert not sym.startswith('OUTLINED_FUNCTION_'), (
685 'Outlined function found in instrumented function, very likely '
686 'something has gone very wrong!')
Matthew Cary91df9792018-11-30 14:35:15687 self._output_data['offsets_kib'] = processor.SymbolsSize(
688 ordered_symbols) / 1024
Matthew Cary78aae162018-08-10 17:16:30689 with open(self._GetUnpatchedOrderfileFilename(), 'w') as orderfile:
Matthew Cary8b1416232018-08-10 19:12:22690 orderfile.write('\n'.join(ordered_symbols))
Matthew Cary78aae162018-08-10 17:16:30691
692 def _CollectLegacyProfile(self):
Matthew Cary2df8d452018-09-19 07:50:20693 files = []
Benoit Lizea3fe2932017-10-20 10:24:52694 try:
Benoit Lizea3fe2932017-10-20 10:24:52695 files = self._profiler.CollectProfile(
696 self._compiler.chrome_apk,
697 constants.PACKAGE_INFO['chrome'])
Matthew Cary69e9e422018-08-10 18:30:06698 self._MaybeSaveProfile(files)
Matthew Caryb8daed942018-06-11 10:58:08699 self._step_recorder.BeginStep('Process profile')
Benoit L96466812018-03-06 15:58:37700 assert os.path.exists(self._compiler.lib_chrome_so)
701 offsets = process_profiles.GetReachedOffsetsFromDumpFiles(
702 files, self._compiler.lib_chrome_so)
703 if not offsets:
704 raise Exception('No profiler offsets found in {}'.format(
705 '\n'.join(files)))
Matthew Cary8b1416232018-08-10 19:12:22706 processor = process_profiles.SymbolOffsetProcessor(
707 self._compiler.lib_chrome_so)
708 ordered_symbols = processor.GetOrderedSymbols(offsets)
709 if not ordered_symbols:
710 raise Exception('No symbol names from offsets found in {}'.format(
711 '\n'.join(files)))
712 with open(self._GetUnpatchedOrderfileFilename(), 'w') as orderfile:
713 orderfile.write('\n'.join(ordered_symbols))
Matthew Cary0f1f681a2018-01-22 10:40:51714 except Exception:
Benoit Lizea3fe2932017-10-20 10:24:52715 for f in files:
716 self._SaveForDebugging(f)
717 raise
718 finally:
719 self._profiler.Cleanup()
Benoit Lizea3fe2932017-10-20 10:24:52720
Matthew Cary69e9e422018-08-10 18:30:06721 def _MaybeSaveProfile(self, files):
722 if self._options.profile_save_dir:
723 logging.info('Saving profiles to %s', self._options.profile_save_dir)
724 for f in files:
725 shutil.copy(f, self._options.profile_save_dir)
726 logging.info('Saved profile %s', f)
727
Benoit Lizea3fe2932017-10-20 10:24:52728 def _PatchOrderfile(self):
729 """Patches the orderfile using clean version of libchrome.so."""
730 self._step_recorder.BeginStep('Patch Orderfile')
Benoit Lizefefbb27c2018-01-17 13:54:18731 patch_orderfile.GeneratePatchedOrderfile(
732 self._GetUnpatchedOrderfileFilename(), self._compiler.lib_chrome_so,
Matthew Caryf949bba2019-02-04 13:39:23733 self._GetPathToOrderfile(), self._order_outlined_functions)
Benoit Lizea3fe2932017-10-20 10:24:52734
735 def _VerifySymbolOrder(self):
736 self._step_recorder.BeginStep('Verify Symbol Order')
737 return_code = self._step_recorder.RunCommand(
738 [self._CHECK_ORDERFILE_SCRIPT, self._compiler.lib_chrome_so,
739 self._GetPathToOrderfile(),
740 '--target-arch=' + self._options.arch],
741 constants.DIR_SOURCE_ROOT,
742 raise_on_error=False)
743 if return_code:
744 self._step_recorder.FailStep('Orderfile check returned %d.' % return_code)
745
746 def _RecordHash(self, file_name):
747 """Records the hash of the file into the output_data dictionary."""
748 self._output_data[os.path.basename(file_name) + '.sha1'] = _GenerateHash(
749 file_name)
750
751 def _SaveFileLocally(self, file_name, file_sha1):
752 """Saves the file to a temporary location and prints the sha1sum."""
753 if not os.path.exists(self._DIRECTORY_FOR_DEBUG_FILES):
754 os.makedirs(self._DIRECTORY_FOR_DEBUG_FILES)
755 shutil.copy(file_name, self._DIRECTORY_FOR_DEBUG_FILES)
756 print 'File: %s, saved in: %s, sha1sum: %s' % (
757 file_name, self._DIRECTORY_FOR_DEBUG_FILES, file_sha1)
758
759 def _SaveForDebugging(self, filename):
760 """Uploads the file to cloud storage or saves to a temporary location."""
761 file_sha1 = _GenerateHash(filename)
762 if not self._options.buildbot:
763 self._SaveFileLocally(filename, file_sha1)
764 else:
765 print 'Uploading file for debugging: ' + filename
766 self._orderfile_updater.UploadToCloudStorage(
767 filename, use_debug_location=True)
768
769 def _SaveForDebuggingWithOverwrite(self, file_name):
770 """Uploads and overwrites the file in cloud storage or copies locally.
771
772 Should be used for large binaries like lib_chrome_so.
773
774 Args:
775 file_name: (str) File to upload.
776 """
777 file_sha1 = _GenerateHash(file_name)
778 if not self._options.buildbot:
779 self._SaveFileLocally(file_name, file_sha1)
780 else:
781 print 'Uploading file for debugging: %s, sha1sum: %s' % (
782 file_name, file_sha1)
783 upload_location = '%s/%s' % (
784 self._CLOUD_STORAGE_BUCKET_FOR_DEBUG, os.path.basename(file_name))
785 self._step_recorder.RunCommand([
786 'gsutil.py', 'cp', file_name, 'gs://' + upload_location])
787 print ('Uploaded to: https://sandbox.google.com/storage/' +
788 upload_location)
789
790 def _MaybeArchiveOrderfile(self, filename):
791 """In buildbot configuration, uploads the generated orderfile to
792 Google Cloud Storage.
793
794 Args:
795 filename: (str) Orderfile to upload.
796 """
Matthew Cary91df9792018-11-30 14:35:15797 # First compute hashes so that we can download them later if we need to.
Benoit Lizea3fe2932017-10-20 10:24:52798 self._step_recorder.BeginStep('Compute hash for ' + filename)
799 self._RecordHash(filename)
800 if self._options.buildbot:
801 self._step_recorder.BeginStep('Archive ' + filename)
802 self._orderfile_updater.UploadToCloudStorage(
803 filename, use_debug_location=False)
804
Egor Paskobce64d012018-11-20 12:02:37805 def UploadReadyOrderfiles(self):
806 self._step_recorder.BeginStep('Upload Ready Orderfiles')
807 for file_name in [self._GetUnpatchedOrderfileFilename(),
808 self._GetPathToOrderfile()]:
809 self._orderfile_updater.UploadToCloudStorage(
810 file_name, use_debug_location=False)
811
Monica Salama90b8f5b2019-04-25 11:10:38812 def _NativeCodeMemoryBenchmark(self, apk):
813 """Runs system_health.memory_mobile to assess native code memory footprint.
814
815 Args:
816 apk: (str) Path to the apk.
817
818 Returns:
819 results: ([int]) Values of native code memory footprint in bytes from the
820 benchmark results.
821 """
822 self._step_recorder.BeginStep("Running orderfile.memory_mobile")
823 try:
824 out_dir = tempfile.mkdtemp()
825 self._profiler._RunCommand(['tools/perf/run_benchmark',
826 '--device={}'.format(
827 self._profiler._device.serial),
828 '--browser=exact',
829 '--output-format=chartjson',
830 '--output-dir={}'.format(out_dir),
831 '--reset-results',
832 '--browser-executable={}'.format(apk),
833 'orderfile.memory_mobile'])
834
835 out_file_path = os.path.join(out_dir, 'results-chart.json')
836 if not os.path.exists(out_file_path):
Monica Basta8ec87fd2019-05-13 12:12:35837 raise Exception('Results file not found!')
Monica Salama90b8f5b2019-04-25 11:10:38838
839 with open(out_file_path, 'r') as f:
840 json_results = json.load(f)
841
842 if not json_results:
Monica Basta8ec87fd2019-05-13 12:12:35843 raise Exception('Results file is empty')
844
845 if not 'charts' in json_results:
846 raise Exception('charts can not be found in results!')
847
848 charts = json_results['charts']
849 results = dict()
850 for story in charts:
851 if not story.endswith("NativeCodeResidentMemory_avg"):
852 continue
853
854 results[story] = dict()
855 for substory in charts[story]:
856 if substory == 'summary':
857 continue
858 if not 'values' in charts[story][substory]:
859 raise Exception(
860 'Values can not be found in charts:%s:%s' % (story, substory))
861
862 results[story][substory] = charts[story][substory]['values']
863 return results
864
865 except Exception as e:
866 return 'Error: ' + str(e)
Monica Salama90b8f5b2019-04-25 11:10:38867
868 finally:
869 shutil.rmtree(out_dir)
870
Monica Salama90b8f5b2019-04-25 11:10:38871
872 def _PerformanceBenchmark(self, apk):
873 """Runs Speedometer2.0 to assess performance.
874
875 Args:
876 apk: (str) Path to the apk.
877
878 Returns:
879 results: ([float]) Speedometer2.0 results samples in milliseconds.
880 """
881 self._step_recorder.BeginStep("Running Speedometer2.0.")
882 try:
883 out_dir = tempfile.mkdtemp()
884 self._profiler._RunCommand(['tools/perf/run_benchmark',
885 '--device={}'.format(
886 self._profiler._device.serial),
887 '--browser=exact',
Monica Basta8ec87fd2019-05-13 12:12:35888 '--output-format=histograms',
Monica Salama90b8f5b2019-04-25 11:10:38889 '--output-dir={}'.format(out_dir),
890 '--reset-results',
891 '--browser-executable={}'.format(apk),
892 'speedometer2'])
893
Monica Basta8ec87fd2019-05-13 12:12:35894 out_file_path = os.path.join(out_dir, 'histograms.json')
Monica Salama90b8f5b2019-04-25 11:10:38895 if not os.path.exists(out_file_path):
Monica Basta8ec87fd2019-05-13 12:12:35896 raise Exception('Results file not found!')
Monica Salama90b8f5b2019-04-25 11:10:38897
898 with open(out_file_path, 'r') as f:
899 results = json.load(f)
900
901 if not results:
Monica Basta8ec87fd2019-05-13 12:12:35902 raise Exception('Results file is empty.')
903
904 for el in results:
905 if 'name' in el and el['name'] == 'Total' and 'sampleValues' in el:
906 return el['sampleValues']
907
908 raise Exception('Unexpected results format.')
909
910 except Exception as e:
911 return 'Error: ' + str(e)
Monica Salama90b8f5b2019-04-25 11:10:38912
913 finally:
914 shutil.rmtree(out_dir)
915
Monica Salama90b8f5b2019-04-25 11:10:38916
917 def RunBenchmark(self, out_directory, no_orderfile=False):
918 """Builds chrome apk and runs performance and memory benchmarks.
919
920 Builds a non-instrumented version of chrome.
921 Installs chrome apk on the device.
922 Runs Speedometer2.0 benchmark to assess performance.
923 Runs system_health.memory_mobile to evaluate memory footprint.
924
925 Args:
926 out_directory: (str) Path to out directory for this build.
927 no_orderfile: (bool) True if chrome to be built without orderfile.
928
929 Returns:
930 benchmark_results: (dict) Results extracted from benchmarks.
931 """
932 try:
933 _UnstashOutputDirectory(out_directory)
934 self._compiler = ClankCompiler(
935 out_directory, self._step_recorder,
936 self._options.arch, self._options.jobs, self._options.max_load,
937 self._options.use_goma, self._options.goma_dir,
938 self._options.system_health_orderfile, self._monochrome,
939 self._options.public, self._GetPathToOrderfile())
940
941 if no_orderfile:
942 orderfile_path = self._GetPathToOrderfile()
943 backup_orderfile = orderfile_path + '.backup'
944 shutil.move(orderfile_path, backup_orderfile)
945 open(orderfile_path, 'w').close()
946
947 # Build APK to be installed on the device.
948 self._compiler.CompileChromeApk(False, force_relink=True)
949 benchmark_results = dict()
950 benchmark_results['Speedometer2.0'] = self._PerformanceBenchmark(
951 self._compiler.chrome_apk)
952 benchmark_results['orderfile.memory_mobile'] = (
953 self._NativeCodeMemoryBenchmark(self._compiler.chrome_apk))
Monica Basta8ec87fd2019-05-13 12:12:35954
955 except Exception as e:
956 benchmark_results['Error'] = str(e)
957
Monica Salama90b8f5b2019-04-25 11:10:38958 finally:
959 if no_orderfile and os.path.exists(backup_orderfile):
960 shutil.move(backup_orderfile, orderfile_path)
961 _StashOutputDirectory(out_directory)
962
963 return benchmark_results
964
Benoit Lizea3fe2932017-10-20 10:24:52965 def Generate(self):
966 """Generates and maybe upload an order."""
967 profile_uploaded = False
968 orderfile_uploaded = False
969
Matthew Carye8400642018-06-14 15:43:02970 assert (bool(self._options.profile) ^
971 bool(self._options.manual_symbol_offsets))
Matthew Cary78aae162018-08-10 17:16:30972 if self._options.system_health_orderfile and not self._options.profile:
973 raise AssertionError('--system_health_orderfile must be not be used '
974 'with --skip-profile')
975 if (self._options.manual_symbol_offsets and
976 not self._options.system_health_orderfile):
977 raise AssertionError('--manual-symbol-offsets must be used with '
978 '--system_health_orderfile.')
Matthew Carye8400642018-06-14 15:43:02979
Benoit Lizea3fe2932017-10-20 10:24:52980 if self._options.profile:
981 try:
982 _UnstashOutputDirectory(self._instrumented_out_dir)
983 self._compiler = ClankCompiler(
984 self._instrumented_out_dir,
985 self._step_recorder, self._options.arch, self._options.jobs,
986 self._options.max_load, self._options.use_goma,
Matthew Caryd6bfcb72018-09-14 11:27:46987 self._options.goma_dir, self._options.system_health_orderfile,
Stephen Kylef11339f2019-03-25 09:00:47988 self._monochrome, self._options.public,
989 self._GetPathToOrderfile())
Matthew Carya2bea452018-11-13 10:21:07990 if not self._options.pregenerated_profiles:
991 # If there are pregenerated profiles, the instrumented build should
992 # not be changed to avoid invalidating the pregenerated profile
993 # offsets.
994 self._compiler.CompileChromeApk(True)
Benoit Lizea3fe2932017-10-20 10:24:52995 self._GenerateAndProcessProfile()
996 self._MaybeArchiveOrderfile(self._GetUnpatchedOrderfileFilename())
997 profile_uploaded = True
998 finally:
Benoit Lizea3fe2932017-10-20 10:24:52999 _StashOutputDirectory(self._instrumented_out_dir)
Matthew Carye8400642018-06-14 15:43:021000 elif self._options.manual_symbol_offsets:
1001 assert self._options.manual_libname
1002 assert self._options.manual_objdir
1003 with file(self._options.manual_symbol_offsets) as f:
1004 symbol_offsets = [int(x) for x in f.xreadlines()]
1005 processor = process_profiles.SymbolOffsetProcessor(
Matthew Caryb46ad282018-11-23 14:43:571006 self._compiler.manual_libname)
Matthew Carye8400642018-06-14 15:43:021007 generator = cyglog_to_orderfile.OffsetOrderfileGenerator(
1008 processor, cyglog_to_orderfile.ObjectFileProcessor(
1009 self._options.manual_objdir))
1010 ordered_sections = generator.GetOrderedSections(symbol_offsets)
1011 if not ordered_sections: # Either None or empty is a problem.
1012 raise Exception('Failed to get ordered sections')
1013 with open(self._GetUnpatchedOrderfileFilename(), 'w') as orderfile:
1014 orderfile.write('\n'.join(ordered_sections))
1015
Benoit Lizea3fe2932017-10-20 10:24:521016 if self._options.patch:
1017 if self._options.profile:
1018 self._RemoveBlanks(self._GetUnpatchedOrderfileFilename(),
1019 self._GetPathToOrderfile())
1020 try:
1021 _UnstashOutputDirectory(self._uninstrumented_out_dir)
1022 self._compiler = ClankCompiler(
1023 self._uninstrumented_out_dir, self._step_recorder,
1024 self._options.arch, self._options.jobs, self._options.max_load,
Matthew Cary78aae162018-08-10 17:16:301025 self._options.use_goma, self._options.goma_dir,
Stephen Kylef11339f2019-03-25 09:00:471026 self._options.system_health_orderfile, self._monochrome,
1027 self._options.public, self._GetPathToOrderfile())
1028
Benoit Lizea3fe2932017-10-20 10:24:521029 self._compiler.CompileLibchrome(False)
1030 self._PatchOrderfile()
1031 # Because identical code folding is a bit different with and without
1032 # the orderfile build, we need to re-patch the orderfile with code
1033 # folding as close to the final version as possible.
1034 self._compiler.CompileLibchrome(False, force_relink=True)
1035 self._PatchOrderfile()
1036 self._compiler.CompileLibchrome(False, force_relink=True)
1037 self._VerifySymbolOrder()
1038 self._MaybeArchiveOrderfile(self._GetPathToOrderfile())
1039 finally:
1040 _StashOutputDirectory(self._uninstrumented_out_dir)
1041 orderfile_uploaded = True
1042
Monica Salama90b8f5b2019-04-25 11:10:381043 if self._options.benchmark:
1044 self._output_data['orderfile_benchmark_results'] = self.RunBenchmark(
1045 self._uninstrumented_out_dir)
1046 self._output_data['no_orderfile_benchmark_results'] = self.RunBenchmark(
1047 self._no_orderfile_out_dir, no_orderfile=True)
1048
Matthew Caryc5ab92a2019-03-19 15:49:241049 if self._options.new_commit_flow:
Matthew Cary86a226e2019-03-19 12:17:441050 self._orderfile_updater._GitStash()
1051 else:
1052 if (self._options.buildbot and self._options.netrc
1053 and not self._step_recorder.ErrorRecorded()):
1054 unpatched_orderfile_filename = (
1055 self._GetUnpatchedOrderfileFilename() if profile_uploaded else None)
1056 orderfile_filename = (
1057 self._GetPathToOrderfile() if orderfile_uploaded else None)
1058 self._orderfile_updater.LegacyCommitFileHashes(
1059 unpatched_orderfile_filename, orderfile_filename)
Benoit Lizea3fe2932017-10-20 10:24:521060 self._step_recorder.EndStep()
1061 return not self._step_recorder.ErrorRecorded()
1062
1063 def GetReportingData(self):
1064 """Get a dictionary of reporting data (timings, output hashes)"""
1065 self._output_data['timings'] = self._step_recorder.timings
1066 return self._output_data
1067
Matthew Cary86a226e2019-03-19 12:17:441068 def CommitStashedOrderfileHashes(self):
1069 """Commit any orderfile hash files in the current checkout.
1070
1071 Only possible if running on the buildbot.
1072
1073 Returns: true on success.
1074 """
1075 if not (self._options.buildbot and self._options.netrc):
1076 logging.error('Trying to commit when not running on the buildbot')
1077 return False
1078 self._orderfile_updater._CommitStashedFiles([
1079 filename + '.sha1'
1080 for filename in (self._GetUnpatchedOrderfileFilename(),
1081 self._GetPathToOrderfile())])
1082 return True
1083
Benoit Lizea3fe2932017-10-20 10:24:521084
Benoit Lizea1b64f82017-12-07 10:12:501085def CreateArgumentParser():
1086 """Creates and returns the argument parser."""
1087 parser = argparse.ArgumentParser()
Monica Salama90b8f5b2019-04-25 11:10:381088 parser.add_argument('--no-benchmark', action='store_false', dest='benchmark',
1089 default=True, help='Disables running benchmarks.')
Benoit Lizea1b64f82017-12-07 10:12:501090 parser.add_argument(
Benoit Lizea3fe2932017-10-20 10:24:521091 '--buildbot', action='store_true',
1092 help='If true, the script expects to be run on a buildbot')
Benoit Lizea1b64f82017-12-07 10:12:501093 parser.add_argument(
Matthew Cary453ff1452018-07-18 12:19:351094 '--device', default=None, type=str,
1095 help='Device serial number on which to run profiling.')
1096 parser.add_argument(
Benoit Lizea3fe2932017-10-20 10:24:521097 '--verify', action='store_true',
1098 help='If true, the script only verifies the current orderfile')
Benoit Lizea1b64f82017-12-07 10:12:501099 parser.add_argument('--target-arch', action='store', dest='arch',
Stephen Martinis71b391b52018-12-04 15:08:041100 default='arm',
Matthew Cary53f74ba2019-01-24 10:07:181101 choices=['arm', 'arm64'],
1102 help='The target architecture for which to build.')
Benoit Lizea1b64f82017-12-07 10:12:501103 parser.add_argument('--output-json', action='store', dest='json_file',
1104 help='Location to save stats in json format')
1105 parser.add_argument(
Benoit Lizea3fe2932017-10-20 10:24:521106 '--skip-profile', action='store_false', dest='profile', default=True,
1107 help='Don\'t generate a profile on the device. Only patch from the '
1108 'existing profile.')
Benoit Lizea1b64f82017-12-07 10:12:501109 parser.add_argument(
Benoit Lizea3fe2932017-10-20 10:24:521110 '--skip-patch', action='store_false', dest='patch', default=True,
1111 help='Only generate the raw (unpatched) orderfile, don\'t patch it.')
Benoit Lizea1b64f82017-12-07 10:12:501112 parser.add_argument(
Benoit Lizea3fe2932017-10-20 10:24:521113 '--netrc', action='store',
1114 help='A custom .netrc file to use for git checkin. Only used on bots.')
Benoit Lizea1b64f82017-12-07 10:12:501115 parser.add_argument(
Benoit Lizea3fe2932017-10-20 10:24:521116 '--branch', action='store', default='master',
1117 help='When running on buildbot with a netrc, the branch orderfile '
1118 'hashes get checked into.')
1119 # Note: -j50 was causing issues on the bot.
Benoit Lizea1b64f82017-12-07 10:12:501120 parser.add_argument(
Benoit Lizea3fe2932017-10-20 10:24:521121 '-j', '--jobs', action='store', default=20,
1122 help='Number of jobs to use for compilation.')
Benoit Lizea1b64f82017-12-07 10:12:501123 parser.add_argument(
Benoit Lizea3fe2932017-10-20 10:24:521124 '-l', '--max-load', action='store', default=4, help='Max cpu load.')
Benoit Lizea1b64f82017-12-07 10:12:501125 parser.add_argument('--goma-dir', help='GOMA directory.')
1126 parser.add_argument(
Benoit Lizea3fe2932017-10-20 10:24:521127 '--use-goma', action='store_true', help='Enable GOMA.', default=False)
Benoit Lizea1b64f82017-12-07 10:12:501128 parser.add_argument('--adb-path', help='Path to the adb binary.')
Matthew Carye8400642018-06-14 15:43:021129
Stephen Kylef11339f2019-03-25 09:00:471130 parser.add_argument('--public', action='store_true',
1131 help='Required if your checkout is non-internal.',
1132 default=False)
Matthew Cary04f41032018-12-10 15:55:271133 parser.add_argument('--nosystem-health-orderfile', action='store_false',
Benoit Lcba421e2019-04-26 15:28:261134 dest='system_health_orderfile', default=True,
Matthew Caryc262f5d2018-09-19 14:36:281135 help=('Create an orderfile based on an about:blank '
1136 'startup benchmark instead of system health '
1137 'benchmarks.'))
Monica Salamaeea2d942019-03-11 12:36:181138 parser.add_argument(
1139 '--use-legacy-chrome-apk', action='store_true', default=False,
1140 help=('Compile and instrument chrome for [L, K] devices.'))
Matthew Cary78aae162018-08-10 17:16:301141
Matthew Carye8400642018-06-14 15:43:021142 parser.add_argument('--manual-symbol-offsets', default=None, type=str,
1143 help=('File of list of ordered symbol offsets generated '
1144 'by manual profiling. Must set other --manual* '
1145 'flags if this is used, and must --skip-profile.'))
1146 parser.add_argument('--manual-libname', default=None, type=str,
1147 help=('Library filename corresponding to '
1148 '--manual-symbol-offsets.'))
1149 parser.add_argument('--manual-objdir', default=None, type=str,
1150 help=('Root of object file directory corresponding to '
1151 '--manual-symbol-offsets.'))
Matthew Caryf949bba2019-02-04 13:39:231152 parser.add_argument('--noorder-outlined-functions', action='store_true',
1153 help='Disable outlined functions in the orderfile.')
Matthew Cary69e9e422018-08-10 18:30:061154 parser.add_argument('--pregenerated-profiles', default=None, type=str,
1155 help=('Pregenerated profiles to use instead of running '
1156 'profile step. Cannot be used with '
1157 '--skip-profiles.'))
1158 parser.add_argument('--profile-save-dir', default=None, type=str,
1159 help=('Directory to save any profiles created. These can '
1160 'be used with --pregenerated-profiles. Cannot be '
1161 'used with --skip-profiles.'))
Egor Paskobce64d012018-11-20 12:02:371162 parser.add_argument('--upload-ready-orderfiles', action='store_true',
1163 help=('Skip orderfile generation and manually upload '
1164 'orderfiles (both patched and unpatched) from '
1165 'their normal location in the tree to the cloud '
1166 'storage. DANGEROUS! USE WITH CARE!'))
Matthew Cary1ea82212019-03-20 12:10:271167 parser.add_argument('--streamline-for-debugging', action='store_true',
1168 help=('Streamline where possible the run for faster '
1169 'iteration while debugging. The orderfile '
1170 'generated will be valid and nontrivial, but '
1171 'may not be based on a representative profile '
1172 'or other such considerations. Use with caution.'))
Matthew Cary86a226e2019-03-19 12:17:441173 parser.add_argument('--commit-hashes', action='store_true',
1174 help=('Commit any orderfile hash files in the current '
1175 'checkout; performs no other action'))
1176 parser.add_argument('--new-commit-flow', action='store_true',
1177 help='Use the new two-step commit flow.')
Matthew Carye8400642018-06-14 15:43:021178
Benoit Lizea1b64f82017-12-07 10:12:501179 profile_android_startup.AddProfileCollectionArguments(parser)
Benoit Lizea3fe2932017-10-20 10:24:521180 return parser
1181
1182
Stephen Kylef11339f2019-03-25 09:00:471183def CreateOrderfile(options, orderfile_updater_class=None):
Benoit Lizea3fe2932017-10-20 10:24:521184 """Creates an oderfile.
1185
1186 Args:
1187 options: As returned from optparse.OptionParser.parse_args()
1188 orderfile_updater_class: (OrderfileUpdater) subclass of OrderfileUpdater.
Stephen Kylef11339f2019-03-25 09:00:471189 Use to explicitly set an OrderfileUpdater class,
1190 the defaults are OrderfileUpdater, or
1191 OrderfileNoopUpdater if --public is set.
Benoit Lizea3fe2932017-10-20 10:24:521192
1193 Returns:
1194 True iff success.
1195 """
1196 logging.basicConfig(level=logging.INFO)
1197 devil_chromium.Initialize(adb_path=options.adb_path)
1198
Egor Pasko93e514e2017-10-31 13:32:361199 generator = OrderfileGenerator(options, orderfile_updater_class)
Benoit Lizea3fe2932017-10-20 10:24:521200 try:
1201 if options.verify:
Egor Pasko93e514e2017-10-31 13:32:361202 generator._VerifySymbolOrder()
Matthew Cary86a226e2019-03-19 12:17:441203 elif options.commit_hashes:
Matthew Caryc5ab92a2019-03-19 15:49:241204 if not options.new_commit_flow:
Matthew Cary86a226e2019-03-19 12:17:441205 raise Exception('--commit-hashes requries --new-commit-flow')
1206 return generator.CommitStashedOrderfileHashes()
Egor Paskobce64d012018-11-20 12:02:371207 elif options.upload_ready_orderfiles:
1208 return generator.UploadReadyOrderfiles()
Benoit Lizea3fe2932017-10-20 10:24:521209 else:
Egor Pasko93e514e2017-10-31 13:32:361210 return generator.Generate()
Benoit Lizea3fe2932017-10-20 10:24:521211 finally:
Egor Pasko93e514e2017-10-31 13:32:361212 json_output = json.dumps(generator.GetReportingData(),
Benoit Lizea3fe2932017-10-20 10:24:521213 indent=2) + '\n'
1214 if options.json_file:
1215 with open(options.json_file, 'w') as f:
1216 f.write(json_output)
1217 print json_output
1218 return False
1219
1220
Benoit Lizea1b64f82017-12-07 10:12:501221def main():
1222 parser = CreateArgumentParser()
1223 options = parser.parse_args()
Stephen Kylef11339f2019-03-25 09:00:471224 return 0 if CreateOrderfile(options) else 1
Benoit Lizea3fe2932017-10-20 10:24:521225
1226
1227if __name__ == '__main__':
Benoit Lizea1b64f82017-12-07 10:12:501228 sys.exit(main())