xref: /aosp_15_r20/external/perfetto/tools/bisect_ui_releases (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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