blob: 00fc5c2a9a72f552c859c576558bccf0de9d8577 [file] [log] [blame]
Primiano Tucci34bc5592021-02-19 17:53:36 +01001#!/usr/bin/env python3
Primiano Tucciac3fd3e2017-09-29 14:29:15 +01002# Copyright (C) 2017 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import argparse
17import os
Hector Dearman0d305872017-10-18 16:18:07 +010018import functools
19import logging
Primiano Tucciac3fd3e2017-09-29 14:29:15 +010020import subprocess
21import sys
Hector Dearman273823c2017-10-13 18:12:27 +010022import time
Primiano Tucciac3fd3e2017-09-29 14:29:15 +010023""" Runs a test executable on Android.
24
25Takes care of pushing the extra shared libraries that might be required by
26some sanitizers. Propagates the test return code to the host, exiting with
270 only if the test execution succeeds on the device.
28"""
29
30ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
31ADB_PATH = os.path.join(ROOT_DIR, 'buildtools/android_sdk/platform-tools/adb')
32
33
Hector Dearman0d305872017-10-18 16:18:07 +010034def RetryOn(exc_type=(), returns_falsy=False, retries=5):
35 """Decorator to retry a function in case of errors or falsy values.
36
37 Implements exponential backoff between retries.
38
39 Args:
40 exc_type: Type of exceptions to catch and retry on. May also pass a tuple
41 of exceptions to catch and retry on any of them. Defaults to catching no
42 exceptions at all.
43 returns_falsy: If True then the function will be retried until it stops
44 returning a "falsy" value (e.g. None, False, 0, [], etc.). If equal to
45 'raise' and the function keeps returning falsy values after all retries,
46 then the decorator will raise a ValueError.
47 retries: Max number of retry attempts. After exhausting that number of
48 attempts the function will be called with no safeguards: any exceptions
49 will be raised and falsy values returned to the caller (except when
50 returns_falsy='raise').
51 """
Primiano Tucci834fdc72019-10-04 11:33:44 +010052
Hector Dearman0d305872017-10-18 16:18:07 +010053 def Decorator(f):
Primiano Tucci834fdc72019-10-04 11:33:44 +010054
Hector Dearman0d305872017-10-18 16:18:07 +010055 @functools.wraps(f)
56 def Wrapper(*args, **kwargs):
57 wait = 1
58 this_retries = kwargs.pop('retries', retries)
59 for _ in range(this_retries):
60 retry_reason = None
61 try:
62 value = f(*args, **kwargs)
63 except exc_type as exc:
64 retry_reason = 'raised %s' % type(exc).__name__
65 if retry_reason is None:
66 if returns_falsy and not value:
67 retry_reason = 'returned %r' % value
68 else:
69 return value # Success!
70 print('{} {}, will retry in {} second{} ...'.format(
71 f.__name__, retry_reason, wait, '' if wait == 1 else 's'))
72 time.sleep(wait)
73 wait *= 2
74 value = f(*args, **kwargs) # Last try to run with no safeguards.
75 if returns_falsy == 'raise' and not value:
76 raise ValueError('%s returned %r' % (f.__name__, value))
77 return value
Primiano Tucci834fdc72019-10-04 11:33:44 +010078
Hector Dearman0d305872017-10-18 16:18:07 +010079 return Wrapper
Primiano Tucci834fdc72019-10-04 11:33:44 +010080
Hector Dearman0d305872017-10-18 16:18:07 +010081 return Decorator
82
83
Primiano Tucciac3fd3e2017-09-29 14:29:15 +010084def AdbCall(*args):
85 cmd = [ADB_PATH] + list(args)
Primiano Tucci34bc5592021-02-19 17:53:36 +010086 print('> adb ' + ' '.join(args))
Primiano Tucciac3fd3e2017-09-29 14:29:15 +010087 return subprocess.check_call(cmd)
88
89
Primiano Tuccid4aa37b2019-08-21 13:38:06 +020090def AdbPush(host, device):
Primiano Tuccie094eec2020-03-18 16:58:21 +000091 if not os.path.exists(host):
92 logging.fatal('Cannot find %s. Was it built?', host)
Primiano Tuccid4aa37b2019-08-21 13:38:06 +020093 cmd = [ADB_PATH, 'push', host, device]
Primiano Tucci34bc5592021-02-19 17:53:36 +010094 print('> adb push ' + ' '.join(cmd[2:]))
95 with open(os.devnull, 'wb') as devnull:
Primiano Tuccid4aa37b2019-08-21 13:38:06 +020096 return subprocess.check_call(cmd, stdout=devnull)
97
98
Hector Dearman273823c2017-10-13 18:12:27 +010099def GetProp(prop):
100 cmd = [ADB_PATH, 'shell', 'getprop', prop]
Primiano Tucci34bc5592021-02-19 17:53:36 +0100101 print('> adb ' + ' '.join(cmd))
102 output = subprocess.check_output(cmd).decode()
Hector Dearman273823c2017-10-13 18:12:27 +0100103 lines = output.splitlines()
Hector Dearman0d305872017-10-18 16:18:07 +0100104 assert len(lines) == 1, 'Expected output to have one line: {}'.format(output)
Primiano Tucci34bc5592021-02-19 17:53:36 +0100105 print(lines[0])
Hector Dearman273823c2017-10-13 18:12:27 +0100106 return lines[0]
107
108
Hector Dearman0d305872017-10-18 16:18:07 +0100109@RetryOn([subprocess.CalledProcessError], returns_falsy=True, retries=10)
110def WaitForBootCompletion():
Hector Dearman273823c2017-10-13 18:12:27 +0100111 return GetProp('sys.boot_completed') == '1'
112
113
Primiano Tuccic6259a92017-11-10 13:51:55 +0000114def EnumerateDataDeps():
Primiano Tucci7a40e4d2017-12-06 09:51:09 +0000115 with open(os.path.join(ROOT_DIR, 'tools', 'test_data.txt')) as f:
Primiano Tuccic6259a92017-11-10 13:51:55 +0000116 lines = f.readlines()
117 for line in (line.strip() for line in lines if not line.startswith('#')):
118 assert os.path.exists(line), line
119 yield line
120
121
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100122def Main():
123 parser = argparse.ArgumentParser()
124 parser.add_argument('--no-cleanup', '-n', action='store_true')
Primiano Tucci480c68b2017-12-19 09:18:00 +0100125 parser.add_argument('--no-data-deps', '-x', action='store_true')
Florian Mayer95d86c62021-02-18 19:09:26 +0000126 parser.add_argument('--system-adb', action='store_true')
Lalit Maganti131b6e52018-03-29 18:29:31 +0100127 parser.add_argument('--env', '-e', action='append')
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100128 parser.add_argument('out_dir', help='out/android/')
Lalit Maganti79f2d7b2018-01-23 18:27:33 +0000129 parser.add_argument('test_name', help='perfetto_unittests')
Sami Kyostila4473d9f2017-11-24 17:45:24 +0000130 parser.add_argument('cmd_args', nargs=argparse.REMAINDER)
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100131 args = parser.parse_args()
132
Florian Mayer95d86c62021-02-18 19:09:26 +0000133 if args.system_adb:
134 global ADB_PATH
135 ADB_PATH = 'adb'
136
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100137 test_bin = os.path.join(args.out_dir, args.test_name)
138 assert os.path.exists(test_bin)
139
Primiano Tucci34bc5592021-02-19 17:53:36 +0100140 print('Waiting for device ...')
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100141 AdbCall('wait-for-device')
Lalit Maganti367fcd52018-02-05 16:06:13 +0000142 # WaitForBootCompletion()
Lalit Maganti79f2d7b2018-01-23 18:27:33 +0000143 AdbCall('root')
144 AdbCall('wait-for-device')
Hector Dearman273823c2017-10-13 18:12:27 +0100145
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100146 target_dir = '/data/local/tmp/' + args.test_name
147 AdbCall('shell', 'rm -rf "%s"; mkdir -p "%s"' % (2 * (target_dir,)))
Stephen Nuskoe46a57b2019-04-15 15:12:05 +0100148 # Some tests require the trace directory to exist, while true for android
149 # devices in general some emulators might not have it set up. So we check to
150 # see if it exists, and if not create it.
Primiano Tuccif0706d12021-01-14 15:20:16 +0100151 trace_dir = '/data/misc/perfetto-traces/bugreport'
Stephen Nuskoe46a57b2019-04-15 15:12:05 +0100152 AdbCall('shell', 'test -d "%s" || mkdir -p "%s"' % (2 * (trace_dir,)))
153 AdbCall('shell', 'rm -rf "%s/*"; ' % trace_dir)
Florian Mayerfd60cc82019-02-06 12:11:09 +0000154 AdbCall('shell', 'mkdir -p /data/nativetest')
Primiano Tucci587750b2020-10-27 15:37:01 +0100155 AdbCall('shell', 'echo 0 > /d/tracing/tracing_on')
156
Florian Mayer2c9d7412019-02-01 16:09:18 +0000157 # This needs to go into /data/nativetest in order to have the system linker
158 # namespace applied, which we need in order to link libdexfile_external.so.
159 # This gets linked into our tests via libundwindstack.so.
160 #
Florian Mayer98b17532019-04-05 17:32:13 +0100161 # See https://android.googlesource.com/platform/system/core/+/master/rootdir/etc/ld.config.txt.
Primiano Tuccid4aa37b2019-08-21 13:38:06 +0200162 AdbPush(test_bin, "/data/nativetest")
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100163
Primiano Tuccie094eec2020-03-18 16:58:21 +0000164 # These two binaries are required to run perfetto_integrationtests.
165 AdbPush(os.path.join(args.out_dir, "perfetto"), "/data/nativetest")
166 AdbPush(os.path.join(args.out_dir, "trigger_perfetto"), "/data/nativetest")
167
Primiano Tucci480c68b2017-12-19 09:18:00 +0100168 if not args.no_data_deps:
169 for dep in EnumerateDataDeps():
Primiano Tuccid4aa37b2019-08-21 13:38:06 +0200170 AdbPush(os.path.join(ROOT_DIR, dep), target_dir + '/' + dep)
Primiano Tuccic6259a92017-11-10 13:51:55 +0000171
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100172 # LLVM sanitizers require to sideload a libclangrtXX.so on the device.
173 sanitizer_libs = os.path.join(args.out_dir, 'sanitizer_libs')
Lalit Maganti131b6e52018-03-29 18:29:31 +0100174 env = ' '.join(args.env if args.env is not None else []) + ' '
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100175 if os.path.exists(sanitizer_libs):
Primiano Tuccid4aa37b2019-08-21 13:38:06 +0200176 AdbPush(sanitizer_libs, target_dir)
Lalit Maganti131b6e52018-03-29 18:29:31 +0100177 env += 'LD_LIBRARY_PATH="%s/sanitizer_libs" ' % (target_dir)
Primiano Tucci834fdc72019-10-04 11:33:44 +0100178 cmd = 'cd %s;' % target_dir
Florian Mayer2c9d7412019-02-01 16:09:18 +0000179 binary = env + '/data/nativetest/%s' % args.test_name
Lalit Magantibfc3d3e2018-03-22 20:28:38 +0000180 cmd += binary
Sami Kyostila4473d9f2017-11-24 17:45:24 +0000181 if args.cmd_args:
Lalit Magantibfc3d3e2018-03-22 20:28:38 +0000182 actual_args = [arg.replace(args.test_name, binary) for arg in args.cmd_args]
183 cmd += ' ' + ' '.join(actual_args)
Primiano Tucci34bc5592021-02-19 17:53:36 +0100184 print(cmd)
185 retcode = subprocess.call([ADB_PATH, 'shell', '-tt', cmd])
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100186 if not args.no_cleanup:
187 AdbCall('shell', 'rm -rf "%s"' % target_dir)
Primiano Tuccid4aa37b2019-08-21 13:38:06 +0200188
189 # Smoke test that adb shell is actually propagating retcode. adb has a history
190 # of breaking this.
Primiano Tucci34bc5592021-02-19 17:53:36 +0100191 test_code = subprocess.call([ADB_PATH, 'shell', '-tt', 'echo Done; exit 42'])
Primiano Tuccid4aa37b2019-08-21 13:38:06 +0200192 if test_code != 42:
193 logging.fatal('adb is incorrectly propagating the exit code')
194 return 1
195
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100196 return retcode
197
198
199if __name__ == '__main__':
200 sys.exit(Main())