1*6dbdd20aSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*6dbdd20aSAndroid Build Coastguard Worker# Copyright (C) 2024 The Android Open Source Project 3*6dbdd20aSAndroid Build Coastguard Worker# 4*6dbdd20aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 5*6dbdd20aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 6*6dbdd20aSAndroid Build Coastguard Worker# You may obtain a copy of the License at 7*6dbdd20aSAndroid Build Coastguard Worker# 8*6dbdd20aSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 9*6dbdd20aSAndroid Build Coastguard Worker# 10*6dbdd20aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 11*6dbdd20aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 12*6dbdd20aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*6dbdd20aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 14*6dbdd20aSAndroid Build Coastguard Worker# limitations under the License. 15*6dbdd20aSAndroid Build Coastguard Worker""" 16*6dbdd20aSAndroid Build Coastguard WorkerRuns a bisection on the autopush ui.perfetto.dev builds 17*6dbdd20aSAndroid Build Coastguard Worker 18*6dbdd20aSAndroid Build Coastguard WorkerSimilar to git bisect, but bisects UI releases rather than commits. 19*6dbdd20aSAndroid Build Coastguard WorkerThis works only for autopush builds from main, ignores canary and stable 20*6dbdd20aSAndroid Build Coastguard Workerchannels, as they make the history non-linear. 21*6dbdd20aSAndroid Build Coastguard Worker 22*6dbdd20aSAndroid Build Coastguard WorkerHow it works: 23*6dbdd20aSAndroid Build Coastguard Worker- It first obtains an unordered list of versions from gs://ui.perfetto.dev 24*6dbdd20aSAndroid Build Coastguard Worker- Then obtains the list of ordered commits from git 25*6dbdd20aSAndroid Build Coastguard Worker- Intersects the two lists, keeping only git commits that have a corresponding 26*6dbdd20aSAndroid Build Coastguard Worker ui autopush release. 27*6dbdd20aSAndroid Build Coastguard Worker- Proceeds with a guided bisect in the range. 28*6dbdd20aSAndroid Build Coastguard Worker""" 29*6dbdd20aSAndroid Build Coastguard Worker 30*6dbdd20aSAndroid Build Coastguard Workerimport argparse 31*6dbdd20aSAndroid Build Coastguard Workerimport sys 32*6dbdd20aSAndroid Build Coastguard Worker 33*6dbdd20aSAndroid Build Coastguard Workerfrom subprocess import check_output 34*6dbdd20aSAndroid Build Coastguard Worker 35*6dbdd20aSAndroid Build Coastguard WorkerCOMMIT_ABBR_LEN = 9 # UI truncates commitish to 9 chars, e.g. v45.0-38b7c2b12. 36*6dbdd20aSAndroid Build Coastguard Worker 37*6dbdd20aSAndroid Build Coastguard Worker 38*6dbdd20aSAndroid Build Coastguard Workerdef main(): 39*6dbdd20aSAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 40*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument( 41*6dbdd20aSAndroid Build Coastguard Worker '--good', 42*6dbdd20aSAndroid Build Coastguard Worker default=None, 43*6dbdd20aSAndroid Build Coastguard Worker help='Last good release (e.g. v44.0-257a02990).' + 44*6dbdd20aSAndroid Build Coastguard Worker 'Defaults to the first verion available') 45*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument( 46*6dbdd20aSAndroid Build Coastguard Worker '--bad', 47*6dbdd20aSAndroid Build Coastguard Worker default=None, 48*6dbdd20aSAndroid Build Coastguard Worker help='First bad release. Defaults to the latest version available') 49*6dbdd20aSAndroid Build Coastguard Worker args = parser.parse_args() 50*6dbdd20aSAndroid Build Coastguard Worker 51*6dbdd20aSAndroid Build Coastguard Worker print('Fetching list of UI releases from GCS...') 52*6dbdd20aSAndroid Build Coastguard Worker rev_list = check_output(['gsutil.py', 'ls', 'gs://ui.perfetto.dev/']).decode() 53*6dbdd20aSAndroid Build Coastguard Worker ui_map = {} # maps '38b7c2b12' -> 'v45.0-38b7c2b12' 54*6dbdd20aSAndroid Build Coastguard Worker for line in rev_list.split(): 55*6dbdd20aSAndroid Build Coastguard Worker version = line.split('/')[3] 56*6dbdd20aSAndroid Build Coastguard Worker if '-' not in version: 57*6dbdd20aSAndroid Build Coastguard Worker continue 58*6dbdd20aSAndroid Build Coastguard Worker ver_hash = version.split('-')[1] 59*6dbdd20aSAndroid Build Coastguard Worker ui_map[ver_hash] = version 60*6dbdd20aSAndroid Build Coastguard Worker print('Found %d UI versions' % len(ui_map)) 61*6dbdd20aSAndroid Build Coastguard Worker 62*6dbdd20aSAndroid Build Coastguard Worker # Get the linear history of all commits. 63*6dbdd20aSAndroid Build Coastguard Worker print('Fetching revision history from git...') 64*6dbdd20aSAndroid Build Coastguard Worker ui_versions = [] 65*6dbdd20aSAndroid Build Coastguard Worker git_out = check_output(['git', 'rev-list', '--left-only', 66*6dbdd20aSAndroid Build Coastguard Worker 'origin/main']).decode() 67*6dbdd20aSAndroid Build Coastguard Worker for line in git_out.split(): 68*6dbdd20aSAndroid Build Coastguard Worker line = line.strip() 69*6dbdd20aSAndroid Build Coastguard Worker rev = line[0:COMMIT_ABBR_LEN] 70*6dbdd20aSAndroid Build Coastguard Worker if rev not in ui_map: 71*6dbdd20aSAndroid Build Coastguard Worker continue # Not all perfetto commits have a UI autopush build. 72*6dbdd20aSAndroid Build Coastguard Worker ui_versions.append(ui_map[rev]) 73*6dbdd20aSAndroid Build Coastguard Worker 74*6dbdd20aSAndroid Build Coastguard Worker # git rev-list emits entries in recent -> older versions. Reverse it. 75*6dbdd20aSAndroid Build Coastguard Worker ui_versions.reverse() 76*6dbdd20aSAndroid Build Coastguard Worker 77*6dbdd20aSAndroid Build Coastguard Worker # Note that not all the entries in ui_map will be present in ui_versions. 78*6dbdd20aSAndroid Build Coastguard Worker # This is because ui_map contains also builds coming from canary and stable 79*6dbdd20aSAndroid Build Coastguard Worker # branches, that we ignore here. 80*6dbdd20aSAndroid Build Coastguard Worker 81*6dbdd20aSAndroid Build Coastguard Worker start = ui_versions.index(args.good) if args.good else 0 82*6dbdd20aSAndroid Build Coastguard Worker end = ui_versions.index(args.bad) if args.bad else len(ui_versions) - 1 83*6dbdd20aSAndroid Build Coastguard Worker while end - start > 1: 84*6dbdd20aSAndroid Build Coastguard Worker print('\033c', end='') # clear terminal. 85*6dbdd20aSAndroid Build Coastguard Worker print( 86*6dbdd20aSAndroid Build Coastguard Worker 'Bisecting from %s (last good) to %s (first bad), %d revisions to go' % 87*6dbdd20aSAndroid Build Coastguard Worker (ui_versions[start], ui_versions[end], end - start + 1)) 88*6dbdd20aSAndroid Build Coastguard Worker mid = (end + start) // 2 89*6dbdd20aSAndroid Build Coastguard Worker 90*6dbdd20aSAndroid Build Coastguard Worker # Print a visual indication of where we are in the bisect. 91*6dbdd20aSAndroid Build Coastguard Worker for i in reversed(range(start, end + 1)): 92*6dbdd20aSAndroid Build Coastguard Worker sfx = '' 93*6dbdd20aSAndroid Build Coastguard Worker if i == start: 94*6dbdd20aSAndroid Build Coastguard Worker sfx = ' GOOD --------------' 95*6dbdd20aSAndroid Build Coastguard Worker elif i == end: 96*6dbdd20aSAndroid Build Coastguard Worker sfx = ' BAD ---------------' 97*6dbdd20aSAndroid Build Coastguard Worker elif i == mid: 98*6dbdd20aSAndroid Build Coastguard Worker sfx = ' <- version to test' 99*6dbdd20aSAndroid Build Coastguard Worker print(ui_versions[i] + sfx) 100*6dbdd20aSAndroid Build Coastguard Worker 101*6dbdd20aSAndroid Build Coastguard Worker user_feedback = input( 102*6dbdd20aSAndroid Build Coastguard Worker 'https://ui.perfetto.dev/%s/. Type g for good and b for bad: ' % 103*6dbdd20aSAndroid Build Coastguard Worker ui_versions[mid]) 104*6dbdd20aSAndroid Build Coastguard Worker if user_feedback == 'b': 105*6dbdd20aSAndroid Build Coastguard Worker end = mid 106*6dbdd20aSAndroid Build Coastguard Worker elif user_feedback == 'g': 107*6dbdd20aSAndroid Build Coastguard Worker start = mid 108*6dbdd20aSAndroid Build Coastguard Worker else: 109*6dbdd20aSAndroid Build Coastguard Worker print('Unrecognised key "%d", try again' % user_feedback) 110*6dbdd20aSAndroid Build Coastguard Worker 111*6dbdd20aSAndroid Build Coastguard Worker print('First bad UI release %s' % ui_versions[end]) 112*6dbdd20aSAndroid Build Coastguard Worker print('You should now inspect the individual commits via git log good..bad') 113*6dbdd20aSAndroid Build Coastguard Worker 114*6dbdd20aSAndroid Build Coastguard Worker 115*6dbdd20aSAndroid Build Coastguard Workerif __name__ == '__main__': 116*6dbdd20aSAndroid Build Coastguard Worker sys.exit(main()) 117