xref: /aosp_15_r20/external/perfetto/tools/run_test_like_ci (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*6dbdd20aSAndroid Build Coastguard Worker# Copyright (C) 2019 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 Workerfrom __future__ import print_function
17*6dbdd20aSAndroid Build Coastguard Workerimport argparse
18*6dbdd20aSAndroid Build Coastguard Workerimport distutils
19*6dbdd20aSAndroid Build Coastguard Workerimport errno
20*6dbdd20aSAndroid Build Coastguard Workerimport grp
21*6dbdd20aSAndroid Build Coastguard Workerimport os
22*6dbdd20aSAndroid Build Coastguard Workerimport readline
23*6dbdd20aSAndroid Build Coastguard Workerimport sys
24*6dbdd20aSAndroid Build Coastguard Workerimport shutil
25*6dbdd20aSAndroid Build Coastguard Workerimport subprocess
26*6dbdd20aSAndroid Build Coastguard Workerfrom pipes import quote
27*6dbdd20aSAndroid Build Coastguard Workerfrom subprocess import check_call
28*6dbdd20aSAndroid Build Coastguard Worker
29*6dbdd20aSAndroid Build Coastguard Workertry:
30*6dbdd20aSAndroid Build Coastguard Worker  from shutil import which as find_executable
31*6dbdd20aSAndroid Build Coastguard Workerexcept AttributeError:
32*6dbdd20aSAndroid Build Coastguard Worker  from distutils.spawn import find_executable
33*6dbdd20aSAndroid Build Coastguard Worker
34*6dbdd20aSAndroid Build Coastguard WorkerREPO_ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
35*6dbdd20aSAndroid Build Coastguard Workersys.path.append(os.path.join(REPO_ROOT, 'infra', 'ci'))
36*6dbdd20aSAndroid Build Coastguard Workerfrom config import JOB_CONFIGS, SANDBOX_IMG
37*6dbdd20aSAndroid Build Coastguard Worker
38*6dbdd20aSAndroid Build Coastguard Workertry:
39*6dbdd20aSAndroid Build Coastguard Worker  input = raw_input
40*6dbdd20aSAndroid Build Coastguard Workerexcept NameError:
41*6dbdd20aSAndroid Build Coastguard Worker  pass
42*6dbdd20aSAndroid Build Coastguard Worker
43*6dbdd20aSAndroid Build Coastguard Worker
44*6dbdd20aSAndroid Build Coastguard Workerdef user_in_docker_group():
45*6dbdd20aSAndroid Build Coastguard Worker  try:
46*6dbdd20aSAndroid Build Coastguard Worker    group = grp.getgrnam('docker')
47*6dbdd20aSAndroid Build Coastguard Worker  except KeyError:
48*6dbdd20aSAndroid Build Coastguard Worker    return False
49*6dbdd20aSAndroid Build Coastguard Worker  else:
50*6dbdd20aSAndroid Build Coastguard Worker    return group.gr_gid in os.getgroups()
51*6dbdd20aSAndroid Build Coastguard Worker
52*6dbdd20aSAndroid Build Coastguard Worker
53*6dbdd20aSAndroid Build Coastguard Workerdef decision(question='Would you like to continue', confirm=True, default='n'):
54*6dbdd20aSAndroid Build Coastguard Worker  default = default.lower().strip()
55*6dbdd20aSAndroid Build Coastguard Worker  yes = default in {'y', 'yes'}
56*6dbdd20aSAndroid Build Coastguard Worker  no = default in {'n', 'no'}
57*6dbdd20aSAndroid Build Coastguard Worker  default = 'y' if yes else 'n'
58*6dbdd20aSAndroid Build Coastguard Worker  prompt = '%s? [%s/%s]: ' % (question, 'Y' if yes else 'y', 'N' if no else 'n')
59*6dbdd20aSAndroid Build Coastguard Worker  if not confirm:
60*6dbdd20aSAndroid Build Coastguard Worker    print('%sy' % prompt)
61*6dbdd20aSAndroid Build Coastguard Worker    return
62*6dbdd20aSAndroid Build Coastguard Worker  while True:
63*6dbdd20aSAndroid Build Coastguard Worker    choice = input(prompt).lower().strip()
64*6dbdd20aSAndroid Build Coastguard Worker    if not choice:
65*6dbdd20aSAndroid Build Coastguard Worker      choice = default
66*6dbdd20aSAndroid Build Coastguard Worker    if choice in {'y', 'yes'}:
67*6dbdd20aSAndroid Build Coastguard Worker      return
68*6dbdd20aSAndroid Build Coastguard Worker    elif choice in {'n', 'no'}:
69*6dbdd20aSAndroid Build Coastguard Worker      sys.exit(3)
70*6dbdd20aSAndroid Build Coastguard Worker
71*6dbdd20aSAndroid Build Coastguard Worker
72*6dbdd20aSAndroid Build Coastguard Workerdef main():
73*6dbdd20aSAndroid Build Coastguard Worker  parser = argparse.ArgumentParser(
74*6dbdd20aSAndroid Build Coastguard Worker      formatter_class=argparse.ArgumentDefaultsHelpFormatter)
75*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument('config', choices=JOB_CONFIGS.keys())
76*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument(
77*6dbdd20aSAndroid Build Coastguard Worker      '--runner',
78*6dbdd20aSAndroid Build Coastguard Worker      help='The container runner executable to use',
79*6dbdd20aSAndroid Build Coastguard Worker      choices=('podman', 'docker'),
80*6dbdd20aSAndroid Build Coastguard Worker      default='podman' if find_executable('podman') else 'docker')
81*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument(
82*6dbdd20aSAndroid Build Coastguard Worker      '--build',
83*6dbdd20aSAndroid Build Coastguard Worker      action='store_true',
84*6dbdd20aSAndroid Build Coastguard Worker      help='Will perform a build of sandbox image')
85*6dbdd20aSAndroid Build Coastguard Worker  group = parser.add_mutually_exclusive_group()
86*6dbdd20aSAndroid Build Coastguard Worker  group.add_argument(
87*6dbdd20aSAndroid Build Coastguard Worker      '--confirm',
88*6dbdd20aSAndroid Build Coastguard Worker      action='store_true',
89*6dbdd20aSAndroid Build Coastguard Worker      default=True,
90*6dbdd20aSAndroid Build Coastguard Worker      help='User confirmation of decision prompts')
91*6dbdd20aSAndroid Build Coastguard Worker  group.add_argument(
92*6dbdd20aSAndroid Build Coastguard Worker      '--no-confirm',
93*6dbdd20aSAndroid Build Coastguard Worker      dest='confirm',
94*6dbdd20aSAndroid Build Coastguard Worker      action='store_false',
95*6dbdd20aSAndroid Build Coastguard Worker      help='Forces confirmation of decision prompts')
96*6dbdd20aSAndroid Build Coastguard Worker  args = parser.parse_args()
97*6dbdd20aSAndroid Build Coastguard Worker
98*6dbdd20aSAndroid Build Coastguard Worker  # Check that the directory is clean.
99*6dbdd20aSAndroid Build Coastguard Worker  git_cmd = ['git', '-C', REPO_ROOT, 'status', '--porcelain']
100*6dbdd20aSAndroid Build Coastguard Worker  modified_files = subprocess.check_output(git_cmd).decode()
101*6dbdd20aSAndroid Build Coastguard Worker  if modified_files:
102*6dbdd20aSAndroid Build Coastguard Worker    print('The current Git repo has modified/untracked files.')
103*6dbdd20aSAndroid Build Coastguard Worker    print('The sandboxed VM will fetch the HEAD of your current git repo.')
104*6dbdd20aSAndroid Build Coastguard Worker    print('This is probably not the state you want to be in.')
105*6dbdd20aSAndroid Build Coastguard Worker    print('I suggest you stop, commit and then re-run this script')
106*6dbdd20aSAndroid Build Coastguard Worker    print('Modified files:\n' + modified_files)
107*6dbdd20aSAndroid Build Coastguard Worker    decision('Do you know what you are doing', confirm=args.confirm)
108*6dbdd20aSAndroid Build Coastguard Worker
109*6dbdd20aSAndroid Build Coastguard Worker  if args.build:
110*6dbdd20aSAndroid Build Coastguard Worker    print('')
111*6dbdd20aSAndroid Build Coastguard Worker    print('About to build %r locally with %r' % (args.image, args.runner))
112*6dbdd20aSAndroid Build Coastguard Worker    decision(confirm=args.confirm)
113*6dbdd20aSAndroid Build Coastguard Worker    check_call(('make', '-C', os.path.join(REPO_ROOT, 'infra', 'ci'),
114*6dbdd20aSAndroid Build Coastguard Worker                'BUILDER=%s' % args.runner, 'build-sandbox'))
115*6dbdd20aSAndroid Build Coastguard Worker
116*6dbdd20aSAndroid Build Coastguard Worker  bundle_path = '/tmp/perfetto-ci.bundle'
117*6dbdd20aSAndroid Build Coastguard Worker  check_call(['git', '-C', REPO_ROOT, 'bundle', 'create', bundle_path, 'HEAD'])
118*6dbdd20aSAndroid Build Coastguard Worker  os.chmod(bundle_path, 0o664)
119*6dbdd20aSAndroid Build Coastguard Worker  env = {
120*6dbdd20aSAndroid Build Coastguard Worker      'PERFETTO_TEST_GIT_REF': bundle_path,
121*6dbdd20aSAndroid Build Coastguard Worker  }
122*6dbdd20aSAndroid Build Coastguard Worker  env.update(JOB_CONFIGS[args.config])
123*6dbdd20aSAndroid Build Coastguard Worker
124*6dbdd20aSAndroid Build Coastguard Worker  workdir = os.path.join(REPO_ROOT, 'out', 'tmp.ci')
125*6dbdd20aSAndroid Build Coastguard Worker  cmd = []
126*6dbdd20aSAndroid Build Coastguard Worker  if args.runner == 'docker' and not user_in_docker_group():
127*6dbdd20aSAndroid Build Coastguard Worker    cmd += ['sudo', '--']
128*6dbdd20aSAndroid Build Coastguard Worker  cmd += [
129*6dbdd20aSAndroid Build Coastguard Worker      args.runner, 'run', '-it', '--name', 'perfetto_ci', '--cap-add',
130*6dbdd20aSAndroid Build Coastguard Worker      'SYS_PTRACE', '--rm', '--volume',
131*6dbdd20aSAndroid Build Coastguard Worker      '%s:/ci/ramdisk' % workdir, '--tmpfs', '/tmp:exec',
132*6dbdd20aSAndroid Build Coastguard Worker      '--volume=%s:%s:ro' % (bundle_path, bundle_path)
133*6dbdd20aSAndroid Build Coastguard Worker  ]
134*6dbdd20aSAndroid Build Coastguard Worker  for kv in env.items():
135*6dbdd20aSAndroid Build Coastguard Worker    cmd += ['--env', '%s=%s' % kv]
136*6dbdd20aSAndroid Build Coastguard Worker  cmd += [SANDBOX_IMG]
137*6dbdd20aSAndroid Build Coastguard Worker  cmd += [
138*6dbdd20aSAndroid Build Coastguard Worker      'bash', '-c',
139*6dbdd20aSAndroid Build Coastguard Worker      'cd /ci/ramdisk; bash /ci/init.sh || sudo -u perfetto -EH bash -i'
140*6dbdd20aSAndroid Build Coastguard Worker  ]
141*6dbdd20aSAndroid Build Coastguard Worker
142*6dbdd20aSAndroid Build Coastguard Worker  print(
143*6dbdd20aSAndroid Build Coastguard Worker      'About to run\n',
144*6dbdd20aSAndroid Build Coastguard Worker      ' '.join('\n  ' + c if c.startswith('--') or c == 'bash' else quote(c)
145*6dbdd20aSAndroid Build Coastguard Worker               for c in cmd))
146*6dbdd20aSAndroid Build Coastguard Worker  print('')
147*6dbdd20aSAndroid Build Coastguard Worker  print('The VM workdir /ci/ramdisk will be mounted into: %s' % workdir)
148*6dbdd20aSAndroid Build Coastguard Worker  print('The contents of %s will be deleted before starting the VM' % workdir)
149*6dbdd20aSAndroid Build Coastguard Worker  decision(confirm=args.confirm)
150*6dbdd20aSAndroid Build Coastguard Worker
151*6dbdd20aSAndroid Build Coastguard Worker  try:
152*6dbdd20aSAndroid Build Coastguard Worker    shutil.rmtree(workdir)
153*6dbdd20aSAndroid Build Coastguard Worker  except EnvironmentError as e:
154*6dbdd20aSAndroid Build Coastguard Worker    if e.errno == errno.ENOENT:
155*6dbdd20aSAndroid Build Coastguard Worker      pass
156*6dbdd20aSAndroid Build Coastguard Worker    elif e.errno == errno.EACCES:
157*6dbdd20aSAndroid Build Coastguard Worker      print('')
158*6dbdd20aSAndroid Build Coastguard Worker      print('Removing previous volume %r' % workdir)
159*6dbdd20aSAndroid Build Coastguard Worker      check_call(('sudo', 'rm', '-r', quote(workdir)))
160*6dbdd20aSAndroid Build Coastguard Worker    else:
161*6dbdd20aSAndroid Build Coastguard Worker      raise
162*6dbdd20aSAndroid Build Coastguard Worker
163*6dbdd20aSAndroid Build Coastguard Worker  os.makedirs(workdir)
164*6dbdd20aSAndroid Build Coastguard Worker  os.execvp(cmd[0], cmd)
165*6dbdd20aSAndroid Build Coastguard Worker
166*6dbdd20aSAndroid Build Coastguard Worker
167*6dbdd20aSAndroid Build Coastguard Workerif __name__ == '__main__':
168*6dbdd20aSAndroid Build Coastguard Worker  sys.exit(main())
169