| # Copyright 2013 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """This script reports time spent by setup.exe in each install/update phase. |
| |
| It does so by probing for InstallerExtraCode1 changes in the registry and can |
| run besides any setup.exe. It's best to launch it before setup.exe itself |
| starts, but can also time remaining stages if launched half-way through. |
| |
| Refer to InstallerStage in chrome/installer/util/util_constants.h for a brief |
| description of each stage. |
| |
| Note that the stages are numbered in the order they were added to setup's |
| implementation, not in the order they are meant to occur. |
| |
| This script never ends, it will endlessly report stage timings until killed. |
| """ |
| |
| import _winreg |
| import json |
| import optparse |
| import sys |
| import time |
| |
| |
| def TimeSetupStages(hive_str, state_key, product_guid, observed_code): |
| """Observes setup.exe and reports about timings for each install/update stage. |
| |
| Does so by observing the registry value |observed_code| in the key at: |
| |hive_str_|\|state_key|\|product_guid|. |
| """ |
| hive = (_winreg.HKEY_LOCAL_MACHINE if hive_str == 'HKLM' else |
| _winreg.HKEY_CURRENT_USER) |
| key = 0 |
| try: |
| key = _winreg.OpenKey(hive, state_key + product_guid, 0, _winreg.KEY_READ) |
| except WindowsError as e: |
| print 'Error opening %s\\%s\\%s: %s' % (hive_str, state_key, product_guid, |
| e) |
| return |
| |
| timings = [] |
| start_time = 0 |
| saw_start = False |
| current_stage = 0 |
| try: |
| current_stage, value_type = _winreg.QueryValueEx(key, observed_code) |
| assert value_type == _winreg.REG_DWORD |
| print 'Starting in already ongoing stage %u' % current_stage |
| start_time = time.clock() |
| except WindowsError: |
| print 'No ongoing stage, waiting for next install/update cycle...' |
| |
| while True: |
| new_stage = 0 |
| try: |
| new_stage, value_type = _winreg.QueryValueEx(key, observed_code) |
| assert value_type == _winreg.REG_DWORD |
| except WindowsError: |
| # Handle the non-existant case by simply leaving |new_stage == 0|. |
| pass |
| if current_stage == new_stage: |
| # Keep probing until a change is seen. |
| time.sleep(0.01) |
| continue |
| |
| if current_stage != 0: |
| # Round elapsed time to 2 digits precision; anything beyond that would be |
| # bogus given the above polling loop's precision. |
| elapsed_time = round(time.clock() - start_time, 2) |
| if saw_start: |
| print '%s: Stage %u took %.2f seconds.' % ( |
| time.strftime("%x %X", time.localtime()), current_stage, |
| elapsed_time) |
| timings.append({'stage': current_stage, 'time': elapsed_time}) |
| else: |
| print '%s: The remainder of stage %u took %.2f seconds.' % ( |
| time.strftime("%x %X", time.localtime()), current_stage, |
| elapsed_time) |
| # Log this timing, but mark that it was already ongoing when this script |
| # started timing it. |
| timings.append({'stage': current_stage, 'time': elapsed_time, |
| 'status': 'missed_start'}) |
| |
| if new_stage != 0: |
| print '%s: Starting stage %u...' % ( |
| time.strftime("%x %X", time.localtime()), new_stage) |
| saw_start = True |
| else: |
| print '%s: Install/update complete, stages timings:' % ( |
| time.strftime("%x %X", time.localtime())) |
| print json.dumps(timings, indent=2, sort_keys=True) |
| timings = [] |
| print '%s: No more stages, waiting for next install/update cycle...' % ( |
| time.strftime("%x %X", time.localtime())) |
| |
| current_stage = new_stage |
| start_time = time.clock() |
| |
| |
| def main(): |
| usage = 'usage: %prog [options]' |
| parser = optparse.OptionParser(usage, |
| description="Times Chrome's installer stages.") |
| parser.add_option('--hive', default='HKLM', |
| help='The hive to observe: "HKLM" for system-level ' |
| 'installs, "HKCU" for user-level installs, defaults ' |
| 'to HKLM.') |
| parser.add_option('--state-key', |
| default='Software\\Google\\Update\\ClientState\\', |
| help="The client state key to observe, defaults to Google " |
| "Update's.") |
| parser.add_option('--product-guid', |
| default='{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}', |
| help="The GUID of the product to observe: defaults to " |
| "the GUID for the Google Chrome Binaries which is the " |
| "one being written to on updates.") |
| parser.add_option('--observed-code', default='InstallerExtraCode1', |
| help='The installer code to observe under ' |
| '|state_key|\\|product_guid|, defaults to ' |
| 'InstallerExtraCode1.') |
| options, _ = parser.parse_args() |
| |
| TimeSetupStages(options.hive, options.state_key, options.product_guid, |
| options.observed_code) |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |