1*6dbdd20aSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*6dbdd20aSAndroid Build Coastguard Worker# Copyright (C) 2017 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 Workerimport argparse 17*6dbdd20aSAndroid Build Coastguard Workerimport os 18*6dbdd20aSAndroid Build Coastguard Workerimport functools 19*6dbdd20aSAndroid Build Coastguard Workerimport logging 20*6dbdd20aSAndroid Build Coastguard Workerimport subprocess 21*6dbdd20aSAndroid Build Coastguard Workerimport sys 22*6dbdd20aSAndroid Build Coastguard Workerimport time 23*6dbdd20aSAndroid Build Coastguard Worker""" Runs a test executable on Android. 24*6dbdd20aSAndroid Build Coastguard Worker 25*6dbdd20aSAndroid Build Coastguard WorkerTakes care of pushing the extra shared libraries that might be required by 26*6dbdd20aSAndroid Build Coastguard Workersome sanitizers. Propagates the test return code to the host, exiting with 27*6dbdd20aSAndroid Build Coastguard Worker0 only if the test execution succeeds on the device. 28*6dbdd20aSAndroid Build Coastguard Worker""" 29*6dbdd20aSAndroid Build Coastguard Worker 30*6dbdd20aSAndroid Build Coastguard WorkerROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 31*6dbdd20aSAndroid Build Coastguard WorkerADB_PATH = os.path.join(ROOT_DIR, 'buildtools/android_sdk/platform-tools/adb') 32*6dbdd20aSAndroid Build Coastguard Worker 33*6dbdd20aSAndroid Build Coastguard Worker 34*6dbdd20aSAndroid Build Coastguard Workerdef RetryOn(exc_type=(), returns_falsy=False, retries=5): 35*6dbdd20aSAndroid Build Coastguard Worker """Decorator to retry a function in case of errors or falsy values. 36*6dbdd20aSAndroid Build Coastguard Worker 37*6dbdd20aSAndroid Build Coastguard Worker Implements exponential backoff between retries. 38*6dbdd20aSAndroid Build Coastguard Worker 39*6dbdd20aSAndroid Build Coastguard Worker Args: 40*6dbdd20aSAndroid Build Coastguard Worker exc_type: Type of exceptions to catch and retry on. May also pass a tuple 41*6dbdd20aSAndroid Build Coastguard Worker of exceptions to catch and retry on any of them. Defaults to catching no 42*6dbdd20aSAndroid Build Coastguard Worker exceptions at all. 43*6dbdd20aSAndroid Build Coastguard Worker returns_falsy: If True then the function will be retried until it stops 44*6dbdd20aSAndroid Build Coastguard Worker returning a "falsy" value (e.g. None, False, 0, [], etc.). If equal to 45*6dbdd20aSAndroid Build Coastguard Worker 'raise' and the function keeps returning falsy values after all retries, 46*6dbdd20aSAndroid Build Coastguard Worker then the decorator will raise a ValueError. 47*6dbdd20aSAndroid Build Coastguard Worker retries: Max number of retry attempts. After exhausting that number of 48*6dbdd20aSAndroid Build Coastguard Worker attempts the function will be called with no safeguards: any exceptions 49*6dbdd20aSAndroid Build Coastguard Worker will be raised and falsy values returned to the caller (except when 50*6dbdd20aSAndroid Build Coastguard Worker returns_falsy='raise'). 51*6dbdd20aSAndroid Build Coastguard Worker """ 52*6dbdd20aSAndroid Build Coastguard Worker 53*6dbdd20aSAndroid Build Coastguard Worker def Decorator(f): 54*6dbdd20aSAndroid Build Coastguard Worker 55*6dbdd20aSAndroid Build Coastguard Worker @functools.wraps(f) 56*6dbdd20aSAndroid Build Coastguard Worker def Wrapper(*args, **kwargs): 57*6dbdd20aSAndroid Build Coastguard Worker wait = 1 58*6dbdd20aSAndroid Build Coastguard Worker this_retries = kwargs.pop('retries', retries) 59*6dbdd20aSAndroid Build Coastguard Worker for _ in range(this_retries): 60*6dbdd20aSAndroid Build Coastguard Worker retry_reason = None 61*6dbdd20aSAndroid Build Coastguard Worker try: 62*6dbdd20aSAndroid Build Coastguard Worker value = f(*args, **kwargs) 63*6dbdd20aSAndroid Build Coastguard Worker except exc_type as exc: 64*6dbdd20aSAndroid Build Coastguard Worker retry_reason = 'raised %s' % type(exc).__name__ 65*6dbdd20aSAndroid Build Coastguard Worker if retry_reason is None: 66*6dbdd20aSAndroid Build Coastguard Worker if returns_falsy and not value: 67*6dbdd20aSAndroid Build Coastguard Worker retry_reason = 'returned %r' % value 68*6dbdd20aSAndroid Build Coastguard Worker else: 69*6dbdd20aSAndroid Build Coastguard Worker return value # Success! 70*6dbdd20aSAndroid Build Coastguard Worker print('{} {}, will retry in {} second{} ...'.format( 71*6dbdd20aSAndroid Build Coastguard Worker f.__name__, retry_reason, wait, '' if wait == 1 else 's')) 72*6dbdd20aSAndroid Build Coastguard Worker time.sleep(wait) 73*6dbdd20aSAndroid Build Coastguard Worker wait *= 2 74*6dbdd20aSAndroid Build Coastguard Worker value = f(*args, **kwargs) # Last try to run with no safeguards. 75*6dbdd20aSAndroid Build Coastguard Worker if returns_falsy == 'raise' and not value: 76*6dbdd20aSAndroid Build Coastguard Worker raise ValueError('%s returned %r' % (f.__name__, value)) 77*6dbdd20aSAndroid Build Coastguard Worker return value 78*6dbdd20aSAndroid Build Coastguard Worker 79*6dbdd20aSAndroid Build Coastguard Worker return Wrapper 80*6dbdd20aSAndroid Build Coastguard Worker 81*6dbdd20aSAndroid Build Coastguard Worker return Decorator 82*6dbdd20aSAndroid Build Coastguard Worker 83*6dbdd20aSAndroid Build Coastguard Worker 84*6dbdd20aSAndroid Build Coastguard Workerdef AdbCall(*args): 85*6dbdd20aSAndroid Build Coastguard Worker cmd = [ADB_PATH] + list(args) 86*6dbdd20aSAndroid Build Coastguard Worker print('> adb ' + ' '.join(args)) 87*6dbdd20aSAndroid Build Coastguard Worker return subprocess.check_call(cmd) 88*6dbdd20aSAndroid Build Coastguard Worker 89*6dbdd20aSAndroid Build Coastguard Worker 90*6dbdd20aSAndroid Build Coastguard Workerdef AdbPush(host, device): 91*6dbdd20aSAndroid Build Coastguard Worker if not os.path.exists(host): 92*6dbdd20aSAndroid Build Coastguard Worker logging.fatal('Cannot find %s. Was it built?', host) 93*6dbdd20aSAndroid Build Coastguard Worker cmd = [ADB_PATH, 'push', host, device] 94*6dbdd20aSAndroid Build Coastguard Worker print('> adb push ' + ' '.join(cmd[2:])) 95*6dbdd20aSAndroid Build Coastguard Worker with open(os.devnull, 'wb') as devnull: 96*6dbdd20aSAndroid Build Coastguard Worker return subprocess.check_call(cmd, stdout=devnull) 97*6dbdd20aSAndroid Build Coastguard Worker 98*6dbdd20aSAndroid Build Coastguard Worker 99*6dbdd20aSAndroid Build Coastguard Workerdef GetProp(prop): 100*6dbdd20aSAndroid Build Coastguard Worker cmd = [ADB_PATH, 'shell', 'getprop', prop] 101*6dbdd20aSAndroid Build Coastguard Worker print('> adb ' + ' '.join(cmd)) 102*6dbdd20aSAndroid Build Coastguard Worker output = subprocess.check_output(cmd).decode() 103*6dbdd20aSAndroid Build Coastguard Worker lines = output.splitlines() 104*6dbdd20aSAndroid Build Coastguard Worker assert len(lines) == 1, 'Expected output to have one line: {}'.format(output) 105*6dbdd20aSAndroid Build Coastguard Worker print(lines[0]) 106*6dbdd20aSAndroid Build Coastguard Worker return lines[0] 107*6dbdd20aSAndroid Build Coastguard Worker 108*6dbdd20aSAndroid Build Coastguard Worker 109*6dbdd20aSAndroid Build Coastguard Worker@RetryOn([subprocess.CalledProcessError], returns_falsy=True, retries=10) 110*6dbdd20aSAndroid Build Coastguard Workerdef WaitForBootCompletion(): 111*6dbdd20aSAndroid Build Coastguard Worker return GetProp('sys.boot_completed') == '1' 112*6dbdd20aSAndroid Build Coastguard Worker 113*6dbdd20aSAndroid Build Coastguard Worker 114*6dbdd20aSAndroid Build Coastguard Workerdef EnumerateDataDeps(): 115*6dbdd20aSAndroid Build Coastguard Worker with open(os.path.join(ROOT_DIR, 'tools', 'test_data.txt')) as f: 116*6dbdd20aSAndroid Build Coastguard Worker lines = f.readlines() 117*6dbdd20aSAndroid Build Coastguard Worker for line in (line.strip() for line in lines if not line.startswith('#')): 118*6dbdd20aSAndroid Build Coastguard Worker assert os.path.exists(line), line 119*6dbdd20aSAndroid Build Coastguard Worker yield line 120*6dbdd20aSAndroid Build Coastguard Worker 121*6dbdd20aSAndroid Build Coastguard Worker 122*6dbdd20aSAndroid Build Coastguard Workerdef Main(): 123*6dbdd20aSAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 124*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--no-cleanup', '-n', action='store_true') 125*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--no-data-deps', '-x', action='store_true') 126*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--system-adb', action='store_true') 127*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--env', '-e', action='append') 128*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('out_dir', help='out/android/') 129*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('test_name', help='perfetto_unittests') 130*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('cmd_args', nargs=argparse.REMAINDER) 131*6dbdd20aSAndroid Build Coastguard Worker args = parser.parse_args() 132*6dbdd20aSAndroid Build Coastguard Worker 133*6dbdd20aSAndroid Build Coastguard Worker if args.system_adb: 134*6dbdd20aSAndroid Build Coastguard Worker global ADB_PATH 135*6dbdd20aSAndroid Build Coastguard Worker ADB_PATH = 'adb' 136*6dbdd20aSAndroid Build Coastguard Worker 137*6dbdd20aSAndroid Build Coastguard Worker test_bin = os.path.join(args.out_dir, args.test_name) 138*6dbdd20aSAndroid Build Coastguard Worker assert os.path.exists(test_bin) 139*6dbdd20aSAndroid Build Coastguard Worker 140*6dbdd20aSAndroid Build Coastguard Worker print('Waiting for device ...') 141*6dbdd20aSAndroid Build Coastguard Worker AdbCall('wait-for-device') 142*6dbdd20aSAndroid Build Coastguard Worker # WaitForBootCompletion() 143*6dbdd20aSAndroid Build Coastguard Worker AdbCall('root') 144*6dbdd20aSAndroid Build Coastguard Worker AdbCall('wait-for-device') 145*6dbdd20aSAndroid Build Coastguard Worker 146*6dbdd20aSAndroid Build Coastguard Worker target_dir = '/data/local/tmp/perfetto_tests' 147*6dbdd20aSAndroid Build Coastguard Worker if not args.no_cleanup: 148*6dbdd20aSAndroid Build Coastguard Worker AdbCall('shell', 'rm -rf "%s"' % target_dir) 149*6dbdd20aSAndroid Build Coastguard Worker AdbCall('shell', 'mkdir -p "%s"' % target_dir) 150*6dbdd20aSAndroid Build Coastguard Worker # Some tests require the trace directory to exist, while true for android 151*6dbdd20aSAndroid Build Coastguard Worker # devices in general some emulators might not have it set up. So we check to 152*6dbdd20aSAndroid Build Coastguard Worker # see if it exists, and if not create it. 153*6dbdd20aSAndroid Build Coastguard Worker trace_dir = '/data/misc/perfetto-traces/bugreport' 154*6dbdd20aSAndroid Build Coastguard Worker AdbCall('shell', 'test -d "%s" || mkdir -p "%s"' % (2 * (trace_dir,))) 155*6dbdd20aSAndroid Build Coastguard Worker AdbCall('shell', 'rm -rf "%s/*"; ' % trace_dir) 156*6dbdd20aSAndroid Build Coastguard Worker AdbCall('shell', 'mkdir -p /data/nativetest') 157*6dbdd20aSAndroid Build Coastguard Worker AdbCall('shell', 'test -f /sys/kernel/tracing/tracing_on ' + 158*6dbdd20aSAndroid Build Coastguard Worker '&& echo 0 > /sys/kernel/tracing/tracing_on || true') 159*6dbdd20aSAndroid Build Coastguard Worker AdbCall('shell', 'test -f /sys/kernel/debug/tracing/tracing_on ' + 160*6dbdd20aSAndroid Build Coastguard Worker '&& echo 0 > /sys/kernel/debug/tracing/tracing_on || true') 161*6dbdd20aSAndroid Build Coastguard Worker 162*6dbdd20aSAndroid Build Coastguard Worker # This needs to go into /data/nativetest in order to have the system linker 163*6dbdd20aSAndroid Build Coastguard Worker # namespace applied, which we need in order to link libdexfile.so. 164*6dbdd20aSAndroid Build Coastguard Worker # This gets linked into our tests via libundwindstack.so. 165*6dbdd20aSAndroid Build Coastguard Worker # 166*6dbdd20aSAndroid Build Coastguard Worker # See https://android.googlesource.com/platform/system/core/+/main/rootdir/etc/ld.config.txt. 167*6dbdd20aSAndroid Build Coastguard Worker AdbPush(test_bin, "/data/nativetest") 168*6dbdd20aSAndroid Build Coastguard Worker 169*6dbdd20aSAndroid Build Coastguard Worker # These two binaries are required to run perfetto_integrationtests. 170*6dbdd20aSAndroid Build Coastguard Worker AdbPush(os.path.join(args.out_dir, "perfetto"), "/data/nativetest") 171*6dbdd20aSAndroid Build Coastguard Worker AdbPush(os.path.join(args.out_dir, "trigger_perfetto"), "/data/nativetest") 172*6dbdd20aSAndroid Build Coastguard Worker 173*6dbdd20aSAndroid Build Coastguard Worker if not args.no_data_deps: 174*6dbdd20aSAndroid Build Coastguard Worker for dep in EnumerateDataDeps(): 175*6dbdd20aSAndroid Build Coastguard Worker AdbPush(os.path.join(ROOT_DIR, dep), target_dir + '/' + dep) 176*6dbdd20aSAndroid Build Coastguard Worker 177*6dbdd20aSAndroid Build Coastguard Worker # LLVM sanitizers require to sideload a libclangrtXX.so on the device. 178*6dbdd20aSAndroid Build Coastguard Worker sanitizer_libs = os.path.join(args.out_dir, 'sanitizer_libs') 179*6dbdd20aSAndroid Build Coastguard Worker env = ' '.join(args.env if args.env is not None else []) + ' ' 180*6dbdd20aSAndroid Build Coastguard Worker if os.path.exists(sanitizer_libs): 181*6dbdd20aSAndroid Build Coastguard Worker AdbPush(sanitizer_libs, target_dir) 182*6dbdd20aSAndroid Build Coastguard Worker env += 'LD_LIBRARY_PATH="%s/sanitizer_libs" ' % (target_dir) 183*6dbdd20aSAndroid Build Coastguard Worker cmd = 'cd %s;' % target_dir 184*6dbdd20aSAndroid Build Coastguard Worker binary = env + '/data/nativetest/%s' % args.test_name 185*6dbdd20aSAndroid Build Coastguard Worker cmd += binary 186*6dbdd20aSAndroid Build Coastguard Worker if args.cmd_args: 187*6dbdd20aSAndroid Build Coastguard Worker actual_args = [arg.replace(args.test_name, binary) for arg in args.cmd_args] 188*6dbdd20aSAndroid Build Coastguard Worker cmd += ' ' + ' '.join(actual_args) 189*6dbdd20aSAndroid Build Coastguard Worker print(cmd) 190*6dbdd20aSAndroid Build Coastguard Worker retcode = subprocess.call([ADB_PATH, 'shell', '-tt', cmd]) 191*6dbdd20aSAndroid Build Coastguard Worker if not args.no_cleanup: 192*6dbdd20aSAndroid Build Coastguard Worker AdbCall('shell', 'rm -rf "%s"' % target_dir) 193*6dbdd20aSAndroid Build Coastguard Worker 194*6dbdd20aSAndroid Build Coastguard Worker # Smoke test that adb shell is actually propagating retcode. adb has a history 195*6dbdd20aSAndroid Build Coastguard Worker # of breaking this. 196*6dbdd20aSAndroid Build Coastguard Worker test_code = subprocess.call([ADB_PATH, 'shell', '-tt', 'echo Done; exit 42']) 197*6dbdd20aSAndroid Build Coastguard Worker if test_code != 42: 198*6dbdd20aSAndroid Build Coastguard Worker logging.fatal('adb is incorrectly propagating the exit code') 199*6dbdd20aSAndroid Build Coastguard Worker return 1 200*6dbdd20aSAndroid Build Coastguard Worker 201*6dbdd20aSAndroid Build Coastguard Worker return retcode 202*6dbdd20aSAndroid Build Coastguard Worker 203*6dbdd20aSAndroid Build Coastguard Worker 204*6dbdd20aSAndroid Build Coastguard Workerif __name__ == '__main__': 205*6dbdd20aSAndroid Build Coastguard Worker sys.exit(Main()) 206