blob: 7a69e9ee566d7b66272591394e71d82b158bbfe4 [file] [log] [blame]
Primiano Tucciac3fd3e2017-09-29 14:29:15 +01001#!/usr/bin/env python
2# 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
18import re
Hector Dearman0d305872017-10-18 16:18:07 +010019import functools
20import logging
Primiano Tucciac3fd3e2017-09-29 14:29:15 +010021import subprocess
22import sys
Primiano Tuccic6259a92017-11-10 13:51:55 +000023import tempfile
Hector Dearman273823c2017-10-13 18:12:27 +010024import time
Primiano Tucciac3fd3e2017-09-29 14:29:15 +010025""" Runs a test executable on Android.
26
27Takes care of pushing the extra shared libraries that might be required by
28some sanitizers. Propagates the test return code to the host, exiting with
290 only if the test execution succeeds on the device.
30"""
31
32ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
33ADB_PATH = os.path.join(ROOT_DIR, 'buildtools/android_sdk/platform-tools/adb')
34
35
Hector Dearman0d305872017-10-18 16:18:07 +010036def RetryOn(exc_type=(), returns_falsy=False, retries=5):
37 """Decorator to retry a function in case of errors or falsy values.
38
39 Implements exponential backoff between retries.
40
41 Args:
42 exc_type: Type of exceptions to catch and retry on. May also pass a tuple
43 of exceptions to catch and retry on any of them. Defaults to catching no
44 exceptions at all.
45 returns_falsy: If True then the function will be retried until it stops
46 returning a "falsy" value (e.g. None, False, 0, [], etc.). If equal to
47 'raise' and the function keeps returning falsy values after all retries,
48 then the decorator will raise a ValueError.
49 retries: Max number of retry attempts. After exhausting that number of
50 attempts the function will be called with no safeguards: any exceptions
51 will be raised and falsy values returned to the caller (except when
52 returns_falsy='raise').
53 """
Primiano Tucci834fdc72019-10-04 11:33:44 +010054
Hector Dearman0d305872017-10-18 16:18:07 +010055 def Decorator(f):
Primiano Tucci834fdc72019-10-04 11:33:44 +010056
Hector Dearman0d305872017-10-18 16:18:07 +010057 @functools.wraps(f)
58 def Wrapper(*args, **kwargs):
59 wait = 1
60 this_retries = kwargs.pop('retries', retries)
61 for _ in range(this_retries):
62 retry_reason = None
63 try:
64 value = f(*args, **kwargs)
65 except exc_type as exc:
66 retry_reason = 'raised %s' % type(exc).__name__
67 if retry_reason is None:
68 if returns_falsy and not value:
69 retry_reason = 'returned %r' % value
70 else:
71 return value # Success!
72 print('{} {}, will retry in {} second{} ...'.format(
73 f.__name__, retry_reason, wait, '' if wait == 1 else 's'))
74 time.sleep(wait)
75 wait *= 2
76 value = f(*args, **kwargs) # Last try to run with no safeguards.
77 if returns_falsy == 'raise' and not value:
78 raise ValueError('%s returned %r' % (f.__name__, value))
79 return value
Primiano Tucci834fdc72019-10-04 11:33:44 +010080
Hector Dearman0d305872017-10-18 16:18:07 +010081 return Wrapper
Primiano Tucci834fdc72019-10-04 11:33:44 +010082
Hector Dearman0d305872017-10-18 16:18:07 +010083 return Decorator
84
85
Primiano Tucciac3fd3e2017-09-29 14:29:15 +010086def AdbCall(*args):
87 cmd = [ADB_PATH] + list(args)
88 print '> adb ' + ' '.join(args)
89 return subprocess.check_call(cmd)
90
91
Primiano Tuccid4aa37b2019-08-21 13:38:06 +020092def AdbPush(host, device):
Primiano Tuccie094eec2020-03-18 16:58:21 +000093 if not os.path.exists(host):
94 logging.fatal('Cannot find %s. Was it built?', host)
Primiano Tuccid4aa37b2019-08-21 13:38:06 +020095 cmd = [ADB_PATH, 'push', host, device]
96 print '> adb push ' + ' '.join(cmd[2:])
97 with open(os.devnull) as devnull:
98 return subprocess.check_call(cmd, stdout=devnull)
99
100
Hector Dearman273823c2017-10-13 18:12:27 +0100101def GetProp(prop):
102 cmd = [ADB_PATH, 'shell', 'getprop', prop]
103 print '> adb ' + ' '.join(cmd)
104 output = subprocess.check_output(cmd)
105 lines = output.splitlines()
Hector Dearman0d305872017-10-18 16:18:07 +0100106 assert len(lines) == 1, 'Expected output to have one line: {}'.format(output)
Hector Dearman273823c2017-10-13 18:12:27 +0100107 print lines[0]
108 return lines[0]
109
110
Hector Dearman0d305872017-10-18 16:18:07 +0100111@RetryOn([subprocess.CalledProcessError], returns_falsy=True, retries=10)
112def WaitForBootCompletion():
Hector Dearman273823c2017-10-13 18:12:27 +0100113 return GetProp('sys.boot_completed') == '1'
114
115
Primiano Tuccic6259a92017-11-10 13:51:55 +0000116def EnumerateDataDeps():
Primiano Tucci7a40e4d2017-12-06 09:51:09 +0000117 with open(os.path.join(ROOT_DIR, 'tools', 'test_data.txt')) as f:
Primiano Tuccic6259a92017-11-10 13:51:55 +0000118 lines = f.readlines()
119 for line in (line.strip() for line in lines if not line.startswith('#')):
120 assert os.path.exists(line), line
121 yield line
122
123
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100124def Main():
125 parser = argparse.ArgumentParser()
126 parser.add_argument('--no-cleanup', '-n', action='store_true')
Primiano Tucci480c68b2017-12-19 09:18:00 +0100127 parser.add_argument('--no-data-deps', '-x', action='store_true')
Florian Mayer95d86c62021-02-18 19:09:26 +0000128 parser.add_argument('--system-adb', action='store_true')
Lalit Maganti131b6e52018-03-29 18:29:31 +0100129 parser.add_argument('--env', '-e', action='append')
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100130 parser.add_argument('out_dir', help='out/android/')
Lalit Maganti79f2d7b2018-01-23 18:27:33 +0000131 parser.add_argument('test_name', help='perfetto_unittests')
Sami Kyostila4473d9f2017-11-24 17:45:24 +0000132 parser.add_argument('cmd_args', nargs=argparse.REMAINDER)
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100133 args = parser.parse_args()
134
Florian Mayer95d86c62021-02-18 19:09:26 +0000135 if args.system_adb:
136 global ADB_PATH
137 ADB_PATH = 'adb'
138
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100139 test_bin = os.path.join(args.out_dir, args.test_name)
140 assert os.path.exists(test_bin)
141
142 print 'Waiting for device ...'
143 AdbCall('wait-for-device')
Lalit Maganti367fcd52018-02-05 16:06:13 +0000144 # WaitForBootCompletion()
Lalit Maganti79f2d7b2018-01-23 18:27:33 +0000145 AdbCall('root')
146 AdbCall('wait-for-device')
Hector Dearman273823c2017-10-13 18:12:27 +0100147
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100148 target_dir = '/data/local/tmp/' + args.test_name
149 AdbCall('shell', 'rm -rf "%s"; mkdir -p "%s"' % (2 * (target_dir,)))
Stephen Nuskoe46a57b2019-04-15 15:12:05 +0100150 # Some tests require the trace directory to exist, while true for android
151 # devices in general some emulators might not have it set up. So we check to
152 # see if it exists, and if not create it.
Primiano Tuccif0706d12021-01-14 15:20:16 +0100153 trace_dir = '/data/misc/perfetto-traces/bugreport'
Stephen Nuskoe46a57b2019-04-15 15:12:05 +0100154 AdbCall('shell', 'test -d "%s" || mkdir -p "%s"' % (2 * (trace_dir,)))
155 AdbCall('shell', 'rm -rf "%s/*"; ' % trace_dir)
Florian Mayerfd60cc82019-02-06 12:11:09 +0000156 AdbCall('shell', 'mkdir -p /data/nativetest')
Primiano Tucci587750b2020-10-27 15:37:01 +0100157 AdbCall('shell', 'echo 0 > /d/tracing/tracing_on')
158
Florian Mayer2c9d7412019-02-01 16:09:18 +0000159 # This needs to go into /data/nativetest in order to have the system linker
160 # namespace applied, which we need in order to link libdexfile_external.so.
161 # This gets linked into our tests via libundwindstack.so.
162 #
Florian Mayer98b17532019-04-05 17:32:13 +0100163 # See https://android.googlesource.com/platform/system/core/+/master/rootdir/etc/ld.config.txt.
Primiano Tuccid4aa37b2019-08-21 13:38:06 +0200164 AdbPush(test_bin, "/data/nativetest")
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100165
Primiano Tuccie094eec2020-03-18 16:58:21 +0000166 # These two binaries are required to run perfetto_integrationtests.
167 AdbPush(os.path.join(args.out_dir, "perfetto"), "/data/nativetest")
168 AdbPush(os.path.join(args.out_dir, "trigger_perfetto"), "/data/nativetest")
169
Primiano Tucci480c68b2017-12-19 09:18:00 +0100170 if not args.no_data_deps:
171 for dep in EnumerateDataDeps():
Primiano Tuccid4aa37b2019-08-21 13:38:06 +0200172 AdbPush(os.path.join(ROOT_DIR, dep), target_dir + '/' + dep)
Primiano Tuccic6259a92017-11-10 13:51:55 +0000173
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100174 # LLVM sanitizers require to sideload a libclangrtXX.so on the device.
175 sanitizer_libs = os.path.join(args.out_dir, 'sanitizer_libs')
Lalit Maganti131b6e52018-03-29 18:29:31 +0100176 env = ' '.join(args.env if args.env is not None else []) + ' '
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100177 if os.path.exists(sanitizer_libs):
Primiano Tuccid4aa37b2019-08-21 13:38:06 +0200178 AdbPush(sanitizer_libs, target_dir)
Lalit Maganti131b6e52018-03-29 18:29:31 +0100179 env += 'LD_LIBRARY_PATH="%s/sanitizer_libs" ' % (target_dir)
Primiano Tucci834fdc72019-10-04 11:33:44 +0100180 cmd = 'cd %s;' % target_dir
Florian Mayer2c9d7412019-02-01 16:09:18 +0000181 binary = env + '/data/nativetest/%s' % args.test_name
Lalit Magantibfc3d3e2018-03-22 20:28:38 +0000182 cmd += binary
Sami Kyostila4473d9f2017-11-24 17:45:24 +0000183 if args.cmd_args:
Lalit Magantibfc3d3e2018-03-22 20:28:38 +0000184 actual_args = [arg.replace(args.test_name, binary) for arg in args.cmd_args]
185 cmd += ' ' + ' '.join(actual_args)
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100186 print cmd
Primiano Tuccid4aa37b2019-08-21 13:38:06 +0200187 retcode = subprocess.call([ADB_PATH, 'shell', cmd])
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100188 if not args.no_cleanup:
189 AdbCall('shell', 'rm -rf "%s"' % target_dir)
Primiano Tuccid4aa37b2019-08-21 13:38:06 +0200190
191 # Smoke test that adb shell is actually propagating retcode. adb has a history
192 # of breaking this.
193 test_code = subprocess.call([ADB_PATH, 'shell', 'echo Done; exit 42'])
194 if test_code != 42:
195 logging.fatal('adb is incorrectly propagating the exit code')
196 return 1
197
Primiano Tucciac3fd3e2017-09-29 14:29:15 +0100198 return retcode
199
200
201if __name__ == '__main__':
202 sys.exit(Main())