Revert "Reland "Android: Switch from asan_device_setup.py -> wrap.sh (requires O MR1)""
This reverts commita3be74b7ad
. Reason for revert: Didn't actually disable lint :/ Original change's description: > Reland "Android: Switch from asan_device_setup.py -> wrap.sh (requires O MR1)" > > This reverts commitf3e1cfa3aa
. > > Reason for reland: Disabling Android lint when ASAN is enabled > > Original change's description: > > Revert "Android: Switch from asan_device_setup.py -> wrap.sh (requires O MR1)" > > > > This reverts commit84403489f3
. > > > > Reason for revert: Lint errors breaking ASAN compiles > > > > Original change's description: > > > Android: Switch from asan_device_setup.py -> wrap.sh (requires O MR1) > > > > > > This bumps the minSdkVersion required for running with is_asan=true to > > > O MR1, but the new mechanism does not require modifying any global state > > > on the device, so is much safer to use. > > > > > > Removes the --tool arg from test_runner.py, since the test runner no > > > longer needs to do anything extra for asan to work. The one thing it > > > does still require is to increase the timeout scale, so this changes to > > > using --timeout-scale directly instead. > > > > > > Also changes ScalableTimeout to being set via instrumentation argument > > > instead of reading its value from /data/local/tmp. > > > > > > And removes @TimeoutScale annotation, which was used only by a single > > > test in order to extend the timeout of @Manual tests. > > > > > > Finally, this extends the timeout for @Manual tests from 10 hours to > > > 1000 hours... because why would we want a timeout on a @Manual test? > > > > > > Bug: 333709824 > > > Change-Id: I4461f05df9143695a8d060f40d9159b45d297cdd > > > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5473125 > > > Reviewed-by: Haiyang Pan <hypan@google.com> > > > Commit-Queue: Andrew Grieve <agrieve@chromium.org> > > > Cr-Commit-Position: refs/heads/main@{#1290797} > > > > Bug: 333709824, 336378247 > > Change-Id: I9cb676d547c63e5d1dff4f583ff9886d4fe4b068 > > No-Presubmit: true > > No-Tree-Checks: true > > No-Try: true > > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5475102 > > Auto-Submit: Andrew Grieve <agrieve@chromium.org> > > Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com> > > Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com> > > Cr-Commit-Position: refs/heads/main@{#1291018} > > Bug: 333709824, 336378247 > Change-Id: If30b954c371a83aa5a30882a54d13f23b2224b7c > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5473570 > Commit-Queue: Haiyang Pan <hypan@google.com> > Auto-Submit: Andrew Grieve <agrieve@chromium.org> > Reviewed-by: Haiyang Pan <hypan@google.com> > Cr-Commit-Position: refs/heads/main@{#1291305} Bug: 333709824, 336378247 Change-Id: I8808f2e18e6875a140df643b6d1a37fc5f657fbc No-Presubmit: true No-Tree-Checks: true No-Try: true Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5479338 Auto-Submit: Andrew Grieve <agrieve@chromium.org> Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com> Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com> Cr-Commit-Position: refs/heads/main@{#1291648}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
f6b1d55505
commit
e2dffc992d
base
build
android
BUILD.gngenerate_wrap_sh.pyvalgrind_tools.pytest_runner.pytest_runner.pydeps
pylib
gtest
instrumentation
local
device
local_device_environment.pylocal_device_gtest_run.pylocal_device_instrumentation_test_run.pylocal_device_test_run.py
local_test_server_spawner.pyconfig
docs
tools/android
@ -4880,6 +4880,7 @@ if (is_android) {
|
||||
"test/android/javatests/src/org/chromium/base/test/util/SkipCheck.java",
|
||||
"test/android/javatests/src/org/chromium/base/test/util/TestFileUtil.java",
|
||||
"test/android/javatests/src/org/chromium/base/test/util/TestThreadUtils.java",
|
||||
"test/android/javatests/src/org/chromium/base/test/util/TimeoutScale.java",
|
||||
"test/android/javatests/src/org/chromium/base/test/util/TimeoutTimer.java",
|
||||
"test/android/javatests/src/org/chromium/base/test/util/UserActionTester.java",
|
||||
"test/android/javatests/src/org/chromium/base/test/util/ViewActionOnDescendant.java",
|
||||
|
@ -39,7 +39,6 @@ import org.chromium.base.test.util.CallbackHelper;
|
||||
import org.chromium.base.test.util.CommandLineFlags;
|
||||
import org.chromium.base.test.util.InMemorySharedPreferencesContext;
|
||||
import org.chromium.base.test.util.MinAndroidSdkLevel;
|
||||
import org.chromium.base.test.util.ScalableTimeout;
|
||||
import org.chromium.build.BuildConfig;
|
||||
import org.chromium.testing.TestListInstrumentationRunListener;
|
||||
|
||||
@ -68,7 +67,6 @@ public class BaseChromiumAndroidJUnitRunner extends AndroidJUnitRunner {
|
||||
private static final String IS_UNIT_TEST_FLAG = "BaseChromiumAndroidJUnitRunner.IsUnitTest";
|
||||
private static final String EXTRA_CLANG_COVERAGE_DEVICE_FILE =
|
||||
"BaseChromiumAndroidJUnitRunner.ClangCoverageDeviceFile";
|
||||
private static final String EXTRA_TIMEOUT_SCALE = "BaseChromiumAndroidJUnitRunner.TimeoutScale";
|
||||
private static final String EXTRA_TRACE_FILE = "BaseChromiumAndroidJUnitRunner.TraceFile";
|
||||
|
||||
private static final String ARGUMENT_LOG_ONLY = "log";
|
||||
@ -151,10 +149,6 @@ public class BaseChromiumAndroidJUnitRunner extends AndroidJUnitRunner {
|
||||
@Override
|
||||
public void onStart() {
|
||||
Bundle arguments = InstrumentationRegistry.getArguments();
|
||||
String timeoutScale = arguments.getString(EXTRA_TIMEOUT_SCALE);
|
||||
if (timeoutScale != null) {
|
||||
ScalableTimeout.setScale(Float.valueOf(timeoutScale));
|
||||
}
|
||||
if (sTestListMode) {
|
||||
Log.w(
|
||||
TAG,
|
||||
|
@ -6,17 +6,24 @@ package org.chromium.base.test.util;
|
||||
|
||||
/**
|
||||
* Utility class for scaling various timeouts by a common factor.
|
||||
*
|
||||
* <p>Set this value via command-line. E.g.: out/Debug/bin/run_tests --timeout-scale=3
|
||||
* For example, to run tests under slow memory tools, you might do
|
||||
* something like this:
|
||||
* adb shell "echo 20.0 > /data/local/tmp/chrome_timeout_scale"
|
||||
*/
|
||||
public class ScalableTimeout {
|
||||
private static float sTimeoutScale = 1;
|
||||
|
||||
public static void setScale(float value) {
|
||||
sTimeoutScale = value;
|
||||
}
|
||||
private static Double sTimeoutScale;
|
||||
public static final String PROPERTY_FILE = "/data/local/tmp/chrome_timeout_scale";
|
||||
|
||||
public static long scaleTimeout(long timeout) {
|
||||
if (sTimeoutScale == null) {
|
||||
try {
|
||||
char[] data = TestFileUtil.readUtf8File(PROPERTY_FILE, 32);
|
||||
sTimeoutScale = Double.parseDouble(new String(data));
|
||||
} catch (Exception e) {
|
||||
// NumberFormatException, FileNotFoundException, IOException
|
||||
sTimeoutScale = 1.0;
|
||||
}
|
||||
}
|
||||
return (long) (timeout * sTimeoutScale);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
// Copyright 2012 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package org.chromium.base.test.util;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/** This annotation can be used to scale a specific test timeout. */
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface TimeoutScale {
|
||||
/**
|
||||
* @return A number to scale the test timeout.
|
||||
*/
|
||||
public int value();
|
||||
}
|
@ -93,18 +93,6 @@ if (enable_java_templates) {
|
||||
}
|
||||
}
|
||||
|
||||
if (defined(sanitizer_arch)) {
|
||||
action("generate_wrap_sh") {
|
||||
script = "generate_wrap_sh.py"
|
||||
outputs = [ "$target_gen_dir/$target_name/wrap.sh" ]
|
||||
args = [
|
||||
"--arch=$sanitizer_arch",
|
||||
"--output",
|
||||
rebase_path(outputs[0], root_build_dir),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# TODO(go/turn-down-test-results): Remove once we turn down
|
||||
# test-results.appspot.com
|
||||
python_library("test_result_presentations_py") {
|
||||
@ -207,6 +195,9 @@ group("test_runner_device_support") {
|
||||
if (enable_chrome_android_internal) {
|
||||
data += [ "//clank/tools/android/avd/proto/" ]
|
||||
}
|
||||
if (is_asan) {
|
||||
data_deps += [ "//tools/android/asan/third_party:asan_device_setup" ]
|
||||
}
|
||||
if (use_full_mte) {
|
||||
data_deps += [ "//tools/android/mte:mte_device_setup" ]
|
||||
}
|
||||
|
@ -1,37 +0,0 @@
|
||||
#! /usr/bin/env python3
|
||||
# Copyright 2024 The Chromium Authors
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
import argparse
|
||||
import pathlib
|
||||
|
||||
|
||||
# Disable memcmp overlap check.There are blobs (gl drivers)
|
||||
# on some android devices that use memcmp on overlapping regions,
|
||||
# nothing we can do about that.
|
||||
#EXTRA_OPTIONS = 'strict_memcmp=0,use_sigaltstack=1'
|
||||
def _generate(arch):
|
||||
return f"""\
|
||||
#!/system/bin/sh
|
||||
# See: https://github.com/google/sanitizers/wiki/AddressSanitizerOnAndroid/\
|
||||
01f8df1ac1a447a8475cdfcb03e8b13140042dbd#running-with-wrapsh-recommended
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
log "Launching with ASAN enabled: $0 $@"
|
||||
export ASAN_OPTIONS=log_to_syslog=false,allow_user_segv_handler=1
|
||||
export LD_PRELOAD=$HERE/libclang_rt.asan-{arch}-android.so
|
||||
exec "$@"
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--arch', required=True)
|
||||
parser.add_argument('--output', required=True)
|
||||
args = parser.parse_args()
|
||||
|
||||
pathlib.Path(args.output).write_text(_generate(args.arch))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -384,9 +384,6 @@ class GtestTestInstance(test_instance.TestInstance):
|
||||
self._extras = {
|
||||
_EXTRA_NATIVE_TEST_ACTIVITY: self._apk_helper.GetActivityName(),
|
||||
}
|
||||
if args.timeout_scale and args.timeout_scale != 1:
|
||||
self._extras[_EXTRA_RUN_IN_SUB_THREAD] = 1
|
||||
|
||||
if self._suite in RUN_IN_SUB_THREAD_TEST_SUITES:
|
||||
self._extras[_EXTRA_RUN_IN_SUB_THREAD] = 1
|
||||
if self._suite in BROWSER_TEST_SUITES:
|
||||
|
@ -22,6 +22,10 @@ _INSTRUMENTATION_TEST_INSTANCE_PATH = (
|
||||
|
||||
class InstrumentationTestInstanceTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
options = mock.Mock()
|
||||
options.tool = ''
|
||||
|
||||
@staticmethod
|
||||
def createTestInstance():
|
||||
c = _INSTRUMENTATION_TEST_INSTANCE_PATH % 'InstrumentationTestInstance'
|
||||
|
@ -108,6 +108,7 @@ class LocalDeviceEnvironment(environment.Environment):
|
||||
self._preferred_abis = None
|
||||
self._recover_devices = args.recover_devices
|
||||
self._skip_clear_data = args.skip_clear_data
|
||||
self._tool_name = args.tool
|
||||
self._trace_output = None
|
||||
# Must check if arg exist because this class is used by
|
||||
# //third_party/catapult's browser_options.py
|
||||
@ -254,6 +255,10 @@ class LocalDeviceEnvironment(environment.Environment):
|
||||
def skip_clear_data(self):
|
||||
return self._skip_clear_data
|
||||
|
||||
@property
|
||||
def tool(self):
|
||||
return self._tool_name
|
||||
|
||||
@property
|
||||
def trace_output(self):
|
||||
return self._trace_output
|
||||
|
@ -358,7 +358,12 @@ class _ExeDelegate:
|
||||
return constants.TEST_EXECUTABLE_DIR
|
||||
|
||||
def Run(self, test, device, flags=None, **kwargs):
|
||||
cmd = [posixpath.join(self._device_dist_dir, self._exe_file_name)]
|
||||
tool = self._test_run.GetTool(device).GetTestWrapper()
|
||||
if tool:
|
||||
cmd = [tool]
|
||||
else:
|
||||
cmd = []
|
||||
cmd.append(posixpath.join(self._device_dist_dir, self._exe_file_name))
|
||||
|
||||
if test:
|
||||
cmd.append('--gtest_filter=%s' % ':'.join(test))
|
||||
@ -368,8 +373,7 @@ class _ExeDelegate:
|
||||
cwd = constants.TEST_EXECUTABLE_DIR
|
||||
|
||||
env = {
|
||||
'LD_LIBRARY_PATH': self._device_dist_dir,
|
||||
'UBSAN_OPTIONS': constants.UBSAN_OPTIONS,
|
||||
'LD_LIBRARY_PATH': self._device_dist_dir
|
||||
}
|
||||
|
||||
if self._coverage_dir:
|
||||
@ -379,6 +383,8 @@ class _ExeDelegate:
|
||||
device_coverage_dir, self._suite, self._coverage_index)
|
||||
self._coverage_index += 1
|
||||
|
||||
if self._env.tool != 'asan':
|
||||
env['UBSAN_OPTIONS'] = constants.UBSAN_OPTIONS
|
||||
|
||||
try:
|
||||
gcov_strip_depth = os.environ['NATIVE_COVERAGE_DEPTH_STRIP']
|
||||
@ -478,7 +484,11 @@ class LocalDeviceGtestRun(local_device_test_run.LocalDeviceTestRun):
|
||||
check_return=True,
|
||||
as_root=self._env.force_main_user)
|
||||
|
||||
def start_servers(dev):
|
||||
def init_tool_and_start_servers(dev):
|
||||
tool = self.GetTool(dev)
|
||||
tool.CopyFiles(dev)
|
||||
tool.SetupEnvironment()
|
||||
|
||||
if self._env.disable_test_server:
|
||||
logging.warning('Not starting test server. Some tests may fail.')
|
||||
return
|
||||
@ -502,7 +512,7 @@ class LocalDeviceGtestRun(local_device_test_run.LocalDeviceTestRun):
|
||||
if self.TestPackage() in _SUITE_REQUIRES_TEST_SERVER_SPAWNER:
|
||||
self._servers[str(dev)].append(
|
||||
local_test_server_spawner.LocalTestServerSpawner(
|
||||
ports.AllocateTestServerPort(), dev))
|
||||
ports.AllocateTestServerPort(), dev, tool))
|
||||
|
||||
for s in self._servers[str(dev)]:
|
||||
s.SetUp()
|
||||
@ -512,8 +522,7 @@ class LocalDeviceGtestRun(local_device_test_run.LocalDeviceTestRun):
|
||||
|
||||
steps = [
|
||||
bind_crash_handler(s, device)
|
||||
for s in (install_apk, push_test_data, start_servers)
|
||||
]
|
||||
for s in (install_apk, push_test_data, init_tool_and_start_servers)]
|
||||
if self._env.concurrent_adb:
|
||||
reraiser_thread.RunAsync(steps)
|
||||
else:
|
||||
@ -761,7 +770,9 @@ class LocalDeviceGtestRun(local_device_test_run.LocalDeviceTestRun):
|
||||
#override
|
||||
def _RunTest(self, device, test):
|
||||
# Run the test.
|
||||
timeout = self._test_instance.shard_timeout * _GetDeviceTimeoutMultiplier()
|
||||
timeout = (self._test_instance.shard_timeout *
|
||||
self.GetTool(device).GetTimeoutScale() *
|
||||
_GetDeviceTimeoutMultiplier())
|
||||
if self._test_instance.wait_for_java_debugger:
|
||||
timeout = None
|
||||
if self._test_instance.store_tombstones:
|
||||
@ -955,4 +966,7 @@ class LocalDeviceGtestRun(local_device_test_run.LocalDeviceTestRun):
|
||||
for s in self._servers.get(str(dev), []):
|
||||
s.TearDown()
|
||||
|
||||
tool = self.GetTool(dev)
|
||||
tool.CleanUpEnvironment()
|
||||
|
||||
self._env.parallel_devices.pMap(individual_device_tear_down)
|
||||
|
@ -30,6 +30,7 @@ from devil.android.tools import webview_app
|
||||
from devil.utils import reraiser_thread
|
||||
from incremental_install import installer
|
||||
from pylib import constants
|
||||
from pylib import valgrind_tools
|
||||
from pylib.base import base_test_result
|
||||
from pylib.base import output_manager
|
||||
from pylib.constants import host_paths
|
||||
@ -69,7 +70,7 @@ _WPR_GO_LINUX_X86_64_PATH = os.path.join(host_paths.DIR_SOURCE_ROOT,
|
||||
_TAG = 'test_runner_py'
|
||||
|
||||
TIMEOUT_ANNOTATIONS = [
|
||||
('Manual', 1000 * 60 * 60),
|
||||
('Manual', 10 * 60 * 60),
|
||||
('IntegrationTest', 10 * 60),
|
||||
('External', 10 * 60),
|
||||
('EnormousTest', 5 * 60),
|
||||
@ -95,8 +96,6 @@ EXTRA_CLANG_COVERAGE_DEVICE_FILE = (
|
||||
EXTRA_SCREENSHOT_FILE = (
|
||||
'org.chromium.base.test.ScreenshotOnFailureStatement.ScreenshotFile')
|
||||
|
||||
EXTRA_TIMEOUT_SCALE = 'BaseChromiumAndroidJUnitRunner.TimeoutScale'
|
||||
|
||||
EXTRA_UI_CAPTURE_DIR = (
|
||||
'org.chromium.base.test.util.Screenshooter.ScreenshotDir')
|
||||
|
||||
@ -599,6 +598,9 @@ class LocalDeviceInstrumentationTestRun(
|
||||
logging.debug('Attempting to set WebView flags: %r', webview_flags)
|
||||
self._webview_flag_changers[str(dev)].AddFlags(webview_flags)
|
||||
|
||||
valgrind_tools.SetChromeTimeoutScale(
|
||||
dev, self._test_instance.timeout_scale)
|
||||
|
||||
install_steps += [push_test_data, create_flag_changer]
|
||||
post_install_steps += [
|
||||
set_debug_app, approve_app_links, set_vega_permissions,
|
||||
@ -687,6 +689,8 @@ class LocalDeviceInstrumentationTestRun(
|
||||
logging.info('Running custom teardown shell command: %s', cmd)
|
||||
dev.RunShellCommand(cmd, shell=True, check_return=True)
|
||||
|
||||
valgrind_tools.SetChromeTimeoutScale(dev, None)
|
||||
|
||||
# If we've force approved app links for a package, undo that now.
|
||||
self._ToggleAppLinks(dev, 'STATE_NO_RESPONSE')
|
||||
|
||||
@ -906,6 +910,7 @@ class LocalDeviceInstrumentationTestRun(
|
||||
extras[_EXTRA_PACKAGE_UNDER_TEST] = package_name
|
||||
|
||||
flags_to_add = []
|
||||
test_timeout_scale = None
|
||||
if self._test_instance.coverage_directory:
|
||||
coverage_basename = '%s' % ('%s_%s_group' %
|
||||
(test[0]['class'], test[0]['method'])
|
||||
@ -1014,10 +1019,11 @@ class LocalDeviceInstrumentationTestRun(
|
||||
timeout = FIXED_TEST_TIMEOUT_OVERHEAD + self._GetTimeoutFromAnnotations(
|
||||
test['annotations'], test_display_name)
|
||||
|
||||
timeout_scale = self._test_instance.timeout_scale * (
|
||||
self._GetTimeoutScaleFromAnnotations(test['annotations']))
|
||||
if timeout_scale != 1:
|
||||
extras[EXTRA_TIMEOUT_SCALE] = str(self._test_instance.timeout_scale)
|
||||
test_timeout_scale = self._GetTimeoutScaleFromAnnotations(
|
||||
test['annotations'])
|
||||
if test_timeout_scale and test_timeout_scale != 1:
|
||||
valgrind_tools.SetChromeTimeoutScale(
|
||||
device, test_timeout_scale * self._test_instance.timeout_scale)
|
||||
|
||||
if self._test_instance.wait_for_java_debugger:
|
||||
timeout = None
|
||||
@ -1100,6 +1106,11 @@ class LocalDeviceInstrumentationTestRun(
|
||||
if flags_to_add:
|
||||
self._flag_changers[str(device)].Restore()
|
||||
|
||||
def restore_timeout_scale():
|
||||
if test_timeout_scale:
|
||||
valgrind_tools.SetChromeTimeoutScale(
|
||||
device, self._test_instance.timeout_scale)
|
||||
|
||||
def handle_coverage_data():
|
||||
if self._test_instance.coverage_directory:
|
||||
try:
|
||||
@ -1214,9 +1225,9 @@ class LocalDeviceInstrumentationTestRun(
|
||||
# the results! Things such as whether the test CRASHED have not yet been
|
||||
# determined.
|
||||
post_test_steps = [
|
||||
restore_flags, stop_chrome_proxy, handle_coverage_data,
|
||||
handle_render_test_data, pull_ui_screen_captures,
|
||||
pull_baseline_profile
|
||||
restore_flags, restore_timeout_scale, stop_chrome_proxy,
|
||||
handle_coverage_data, handle_render_test_data,
|
||||
pull_ui_screen_captures, pull_baseline_profile
|
||||
]
|
||||
if self._env.concurrent_adb:
|
||||
reraiser_thread.RunAsync(post_test_steps)
|
||||
@ -1380,9 +1391,6 @@ class LocalDeviceInstrumentationTestRun(
|
||||
# Workaround for https://github.com/mockito/mockito/issues/922
|
||||
'notPackage': 'net.bytebuddy',
|
||||
}
|
||||
if self._test_instance.timeout_scale != 1:
|
||||
extras[EXTRA_TIMEOUT_SCALE] = str(self._test_instance.timeout_scale)
|
||||
|
||||
# BaseChromiumAndroidJUnitRunner ignores this bundle value (and always
|
||||
# adds the listener). This is needed to enable the the listener when
|
||||
# using AndroidJUnitRunner directly.
|
||||
|
@ -19,6 +19,7 @@ from devil.android import device_errors
|
||||
from devil.android.sdk import version_codes
|
||||
from devil.android.tools import device_recovery
|
||||
from devil.utils import signal_handler
|
||||
from pylib import valgrind_tools
|
||||
from pylib.base import base_test_result
|
||||
from pylib.base import test_collection
|
||||
from pylib.base import test_exception
|
||||
@ -47,6 +48,7 @@ class LocalDeviceTestRun(test_run.TestRun):
|
||||
|
||||
def __init__(self, env, test_instance):
|
||||
super().__init__(env, test_instance)
|
||||
self._tools = {}
|
||||
# This is intended to be filled by a child class.
|
||||
self._installed_packages = []
|
||||
env.SetPreferredAbis(test_instance.GetPreferredAbis())
|
||||
@ -358,6 +360,12 @@ class LocalDeviceTestRun(test_run.TestRun):
|
||||
return ('Batch' not in annotations
|
||||
or annotations['Batch']['value'] != 'UnitTests')
|
||||
|
||||
def GetTool(self, device):
|
||||
if str(device) not in self._tools:
|
||||
self._tools[str(device)] = valgrind_tools.CreateTool(
|
||||
self._env.tool, device)
|
||||
return self._tools[str(device)]
|
||||
|
||||
def _CreateShardsForDevices(self, tests):
|
||||
raise NotImplementedError
|
||||
|
||||
|
@ -35,12 +35,12 @@ def _WaitUntil(predicate, max_attempts=5):
|
||||
|
||||
|
||||
class PortForwarderAndroid(chrome_test_server_spawner.PortForwarder):
|
||||
|
||||
def __init__(self, device):
|
||||
def __init__(self, device, tool):
|
||||
self.device = device
|
||||
self.tool = tool
|
||||
|
||||
def Map(self, port_pairs):
|
||||
forwarder.Forwarder.Map(port_pairs, self.device)
|
||||
forwarder.Forwarder.Map(port_pairs, self.device, self.tool)
|
||||
|
||||
def GetDevicePortForHostPort(self, host_port):
|
||||
return forwarder.Forwarder.DevicePortForHostPort(host_port)
|
||||
@ -60,11 +60,12 @@ class PortForwarderAndroid(chrome_test_server_spawner.PortForwarder):
|
||||
|
||||
class LocalTestServerSpawner(test_server.TestServer):
|
||||
|
||||
def __init__(self, port, device):
|
||||
def __init__(self, port, device, tool):
|
||||
super().__init__()
|
||||
self._device = device
|
||||
self._spawning_server = chrome_test_server_spawner.SpawningServer(
|
||||
port, PortForwarderAndroid(device), MAX_TEST_SERVER_INSTANCES)
|
||||
port, PortForwarderAndroid(device, tool), MAX_TEST_SERVER_INSTANCES)
|
||||
self._tool = tool
|
||||
|
||||
@property
|
||||
def server_address(self):
|
||||
@ -84,7 +85,8 @@ class LocalTestServerSpawner(test_server.TestServer):
|
||||
self._device.WriteFile(
|
||||
'%s/net-test-server-config' % self._device.GetExternalStoragePath(),
|
||||
test_server_config)
|
||||
forwarder.Forwarder.Map([(self.port, self.port)], self._device)
|
||||
forwarder.Forwarder.Map(
|
||||
[(self.port, self.port)], self._device, self._tool)
|
||||
self._spawning_server.Start()
|
||||
|
||||
#override
|
||||
|
116
build/android/pylib/valgrind_tools.py
Normal file
116
build/android/pylib/valgrind_tools.py
Normal file
@ -0,0 +1,116 @@
|
||||
# Copyright 2012 The Chromium Authors
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
# pylint: disable=R0201
|
||||
|
||||
|
||||
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from devil.android import device_errors
|
||||
from devil.android.valgrind_tools import base_tool
|
||||
|
||||
|
||||
def SetChromeTimeoutScale(device, scale):
|
||||
"""Sets the timeout scale in /data/local/tmp/chrome_timeout_scale to scale."""
|
||||
path = '/data/local/tmp/chrome_timeout_scale'
|
||||
if not scale or scale == 1.0:
|
||||
# Delete if scale is None/0.0/1.0 since the default timeout scale is 1.0
|
||||
device.RemovePath(path, force=True, as_root=True)
|
||||
else:
|
||||
device.WriteFile(path, '%f' % scale, as_root=True)
|
||||
|
||||
|
||||
|
||||
class AddressSanitizerTool(base_tool.BaseTool):
|
||||
"""AddressSanitizer tool."""
|
||||
|
||||
WRAPPER_NAME = '/system/bin/asanwrapper'
|
||||
# Disable memcmp overlap check.There are blobs (gl drivers)
|
||||
# on some android devices that use memcmp on overlapping regions,
|
||||
# nothing we can do about that.
|
||||
EXTRA_OPTIONS = 'strict_memcmp=0,use_sigaltstack=1'
|
||||
|
||||
def __init__(self, device):
|
||||
super().__init__()
|
||||
self._device = device
|
||||
|
||||
@classmethod
|
||||
def CopyFiles(cls, device):
|
||||
"""Copies ASan tools to the device."""
|
||||
del device
|
||||
|
||||
def GetTestWrapper(self):
|
||||
return AddressSanitizerTool.WRAPPER_NAME
|
||||
|
||||
def GetUtilWrapper(self):
|
||||
"""Returns the wrapper for utilities, such as forwarder.
|
||||
|
||||
AddressSanitizer wrapper must be added to all instrumented binaries,
|
||||
including forwarder and the like. This can be removed if such binaries
|
||||
were built without instrumentation. """
|
||||
return self.GetTestWrapper()
|
||||
|
||||
def SetupEnvironment(self):
|
||||
try:
|
||||
self._device.EnableRoot()
|
||||
except device_errors.CommandFailedError as e:
|
||||
# Try to set the timeout scale anyway.
|
||||
# TODO(jbudorick) Handle this exception appropriately after interface
|
||||
# conversions are finished.
|
||||
logging.error(str(e))
|
||||
SetChromeTimeoutScale(self._device, self.GetTimeoutScale())
|
||||
|
||||
def CleanUpEnvironment(self):
|
||||
SetChromeTimeoutScale(self._device, None)
|
||||
|
||||
def GetTimeoutScale(self):
|
||||
# Very slow startup.
|
||||
return 20.0
|
||||
|
||||
|
||||
TOOL_REGISTRY = {
|
||||
'asan': AddressSanitizerTool,
|
||||
}
|
||||
|
||||
|
||||
def CreateTool(tool_name, device):
|
||||
"""Creates a tool with the specified tool name.
|
||||
|
||||
Args:
|
||||
tool_name: Name of the tool to create.
|
||||
device: A DeviceUtils instance.
|
||||
Returns:
|
||||
A tool for the specified tool_name.
|
||||
"""
|
||||
if not tool_name:
|
||||
return base_tool.BaseTool()
|
||||
|
||||
ctor = TOOL_REGISTRY.get(tool_name)
|
||||
if ctor:
|
||||
return ctor(device)
|
||||
print('Unknown tool %s, available tools: %s' %
|
||||
(tool_name, ', '.join(sorted(TOOL_REGISTRY.keys()))))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def PushFilesForTool(tool_name, device):
|
||||
"""Pushes the files required for |tool_name| to |device|.
|
||||
|
||||
Args:
|
||||
tool_name: Name of the tool to create.
|
||||
device: A DeviceUtils instance.
|
||||
"""
|
||||
if not tool_name:
|
||||
return
|
||||
|
||||
clazz = TOOL_REGISTRY.get(tool_name)
|
||||
if clazz:
|
||||
clazz.CopyFiles(device)
|
||||
else:
|
||||
print('Unknown tool %s, available tools: %s' % (tool_name, ', '.join(
|
||||
sorted(TOOL_REGISTRY.keys()))))
|
||||
sys.exit(1)
|
@ -270,9 +270,6 @@ def AddCommonOptions(parser):
|
||||
help='If present, store test results on this path.')
|
||||
parser.add_argument('--isolated-script-test-perf-output',
|
||||
help='If present, store chartjson results on this path.')
|
||||
parser.add_argument('--timeout-scale',
|
||||
type=float,
|
||||
help='Factor by which timeouts should be scaled.')
|
||||
|
||||
AddTestLauncherOptions(parser)
|
||||
|
||||
@ -332,7 +329,12 @@ def AddDeviceOptions(parser):
|
||||
'--recover-devices',
|
||||
action='store_true',
|
||||
help='Attempt to recover devices prior to the final retry. Warning: '
|
||||
'this will cause all devices to reboot.')
|
||||
'this will cause all devices to reboot.')
|
||||
parser.add_argument(
|
||||
'--tool',
|
||||
dest='tool',
|
||||
help='Run the test under a tool '
|
||||
'(use --tool help to list them)')
|
||||
|
||||
parser.add_argument(
|
||||
'--upload-logcats-file',
|
||||
@ -648,6 +650,10 @@ def AddInstrumentationTestOptions(parser):
|
||||
help=('Not actually used for instrumentation tests, but can be used as '
|
||||
'a proxy for determining if the current run is a retry without '
|
||||
'patch.'))
|
||||
parser.add_argument(
|
||||
'--timeout-scale',
|
||||
type=float,
|
||||
help='Factor by which timeouts should be scaled.')
|
||||
parser.add_argument(
|
||||
'--is-unit-test',
|
||||
action='store_true',
|
||||
|
@ -228,5 +228,6 @@ pylib/utils/logging_utils.py
|
||||
pylib/utils/repo_utils.py
|
||||
pylib/utils/test_filter.py
|
||||
pylib/utils/time_profile.py
|
||||
pylib/valgrind_tools.py
|
||||
test_runner.py
|
||||
tombstones.py
|
||||
|
@ -28,7 +28,6 @@ if (is_android || is_chromeos) {
|
||||
import("//build/config/android/channel.gni")
|
||||
import("//build/config/clang/clang.gni")
|
||||
import("//build/config/dcheck_always_on.gni")
|
||||
import("//build/config/sanitizers/sanitizers.gni")
|
||||
import("//build/toolchain/siso.gni")
|
||||
import("//build_overrides/build.gni")
|
||||
import("abi.gni")
|
||||
@ -68,9 +67,6 @@ if (is_android || is_chromeos) {
|
||||
# The default to use for android:minSdkVersion for targets that do
|
||||
# not explicitly set it.
|
||||
default_min_sdk_version = 26
|
||||
if (is_asan) {
|
||||
default_min_sdk_version = 27
|
||||
}
|
||||
|
||||
# Static analysis can be either "on" or "off" or "build_server". This
|
||||
# controls how android lint, error-prone, bytecode checks are run. This
|
||||
@ -93,15 +89,6 @@ if (is_android || is_chromeos) {
|
||||
# Our build system no longer supports legacy multidex.
|
||||
min_supported_sdk_version = 21
|
||||
|
||||
# ASAN requireds O MR1.
|
||||
# https://github.com/google/sanitizers/wiki/AddressSanitizerOnAndroid/01f8df1ac1a447a8475cdfcb03e8b13140042dbd#running-with-wrapsh-recommended
|
||||
if (is_asan) {
|
||||
min_supported_sdk_version = 27
|
||||
|
||||
# Disable lint since increasing min_sdk_version can cause ObsoleteSdkInt warnings.
|
||||
disable_android_lint = true
|
||||
}
|
||||
|
||||
assert(
|
||||
default_min_sdk_version >= min_supported_sdk_version,
|
||||
"default_min_sdk_version ($default_min_sdk_version) must be >= min_supported_sdk_version ($min_supported_sdk_version)")
|
||||
|
@ -990,7 +990,7 @@ template("test_runner_script") {
|
||||
executable_args += [ "--fast-local-dev" ]
|
||||
}
|
||||
if (_device_test && is_asan) {
|
||||
executable_args += [ "--timeout-scale=4" ]
|
||||
executable_args += [ "--tool=asan" ]
|
||||
}
|
||||
|
||||
if (defined(invoker.modules)) {
|
||||
|
@ -33,9 +33,6 @@ if (is_robolectric) {
|
||||
if (use_cfi_diag || is_ubsan || is_ubsan_security || is_ubsan_vptr) {
|
||||
_sanitizer_runtimes += [ "$clang_base_path/lib/clang/$clang_version/lib/linux/libclang_rt.ubsan_standalone-$sanitizer_arch-android.so" ]
|
||||
}
|
||||
if (is_asan) {
|
||||
_sanitizer_runtimes += [ "$clang_base_path/lib/clang/$clang_version/lib/linux/libclang_rt.asan-$sanitizer_arch-android.so" ]
|
||||
}
|
||||
|
||||
# Creates a dist directory for a native executable.
|
||||
#
|
||||
@ -2106,9 +2103,6 @@ if (is_robolectric) {
|
||||
if (defined(invoker.min_sdk_version)) {
|
||||
_min_sdk_version = invoker.min_sdk_version
|
||||
}
|
||||
if (is_asan && _min_sdk_version < min_supported_sdk_version) {
|
||||
_min_sdk_version = min_supported_sdk_version
|
||||
}
|
||||
if (defined(invoker.target_sdk_version)) {
|
||||
_target_sdk_version = invoker.target_sdk_version
|
||||
}
|
||||
@ -2586,16 +2580,9 @@ if (is_robolectric) {
|
||||
if (defined(invoker.loadable_modules)) {
|
||||
_loadable_modules = invoker.loadable_modules
|
||||
}
|
||||
_sanitizer_loadable_modules = []
|
||||
_sanitizer_deps = []
|
||||
if (_is_base_module && _native_libs_deps != [] && !_uses_static_library) {
|
||||
_sanitizer_loadable_modules += _sanitizer_runtimes
|
||||
}
|
||||
if (is_asan && _is_base_module &&
|
||||
(_uses_static_library || _native_libs_deps != [])) {
|
||||
_sanitizer_loadable_modules +=
|
||||
[ "$root_gen_dir/build/android/generate_wrap_sh/wrap.sh" ]
|
||||
_sanitizer_deps += [ "//build/android:generate_wrap_sh" ]
|
||||
|
||||
if (_native_libs_deps != []) {
|
||||
_loadable_modules += _sanitizer_runtimes
|
||||
}
|
||||
|
||||
_assertions_implicitly_enabled = defined(invoker.custom_assertion_handler)
|
||||
@ -2744,11 +2731,7 @@ if (is_robolectric) {
|
||||
_secondary_abi_shared_library_list_file
|
||||
}
|
||||
|
||||
if (!defined(deps)) {
|
||||
deps = []
|
||||
}
|
||||
deps += _sanitizer_deps
|
||||
loadable_modules = _loadable_modules + _sanitizer_loadable_modules
|
||||
loadable_modules = _loadable_modules
|
||||
|
||||
if (defined(_allowlist_r_txt_path) && _is_bundle_module) {
|
||||
# Used to write the file path to the target's .build_config.json only.
|
||||
@ -2957,7 +2940,7 @@ if (is_robolectric) {
|
||||
|
||||
# Need full deps rather than _non_java_deps, because loadable_modules
|
||||
# may include .so files extracted by __unpack_aar targets.
|
||||
deps = _invoker_deps + _sanitizer_deps + [ ":$_build_config_target" ]
|
||||
deps = _invoker_deps + [ ":$_build_config_target" ]
|
||||
if (defined(invoker.asset_deps)) {
|
||||
deps += invoker.asset_deps
|
||||
}
|
||||
@ -2976,19 +2959,15 @@ if (is_robolectric) {
|
||||
# should be clearly named/labeled "incremental".
|
||||
output_apk_path = _incremental_apk_path
|
||||
|
||||
loadable_modules = _sanitizer_loadable_modules
|
||||
|
||||
# All native libraries are side-loaded, so use a placeholder to force
|
||||
# the proper bitness for the app.
|
||||
_has_native_libs =
|
||||
defined(_native_libs_filearg) || _loadable_modules != [] ||
|
||||
_sanitizer_loadable_modules != []
|
||||
if (_has_native_libs && loadable_modules == [] &&
|
||||
!defined(native_lib_placeholders)) {
|
||||
defined(_native_libs_filearg) || _loadable_modules != []
|
||||
if (_has_native_libs && !defined(native_lib_placeholders)) {
|
||||
native_lib_placeholders = [ "libfix.crbug.384638.so" ]
|
||||
}
|
||||
} else {
|
||||
loadable_modules = _loadable_modules + _sanitizer_loadable_modules
|
||||
loadable_modules = _loadable_modules
|
||||
deps += _all_native_libs_deps + [
|
||||
":$_compile_resources_target",
|
||||
":$_merge_manifest_target",
|
||||
@ -4516,9 +4495,6 @@ if (is_robolectric) {
|
||||
if (defined(invoker.min_sdk_version)) {
|
||||
_min_sdk_version = invoker.min_sdk_version
|
||||
}
|
||||
if (is_asan && _min_sdk_version < min_supported_sdk_version) {
|
||||
_min_sdk_version = min_supported_sdk_version
|
||||
}
|
||||
|
||||
_bundle_base_path = "$root_build_dir/apks"
|
||||
if (defined(invoker.bundle_base_path)) {
|
||||
|
25
docs/asan.md
25
docs/asan.md
@ -200,6 +200,31 @@ is_asan=true
|
||||
is_debug=false
|
||||
```
|
||||
|
||||
Running ASan applications on Android requires additional device setup. Chromium
|
||||
testing scripts take care of this, so testing works as expected:
|
||||
```shell
|
||||
build/android/test_runner.py instrumentation --test-apk ContentShellTest \
|
||||
--test_data content:content/test/data/android/device_files -v -v -v \
|
||||
--tool=asan --release
|
||||
```
|
||||
|
||||
If the above step fails or to run stuff without Chromium testing script (ex.
|
||||
ContentShell.apk, or any third party apk or binary), device setup is needed:
|
||||
```shell
|
||||
tools/android/asan/third_party/asan_device_setup.sh \
|
||||
--lib third_party/android_toolchain/ndk/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang/*/lib/linux
|
||||
# wait a few seconds for the device to reload
|
||||
```
|
||||
It only needs to be run once per device. It is safe to run it multiple times.
|
||||
Examine the output to ensure that setup was successful (you may need to run
|
||||
`adb disable-verity` and restart the device first). When this is done, the
|
||||
device will run ASan apks as well as normal apks without any further setup.
|
||||
|
||||
To run command-line tools (i.e. binaries), prefix them with `asanwrapper`:
|
||||
```shell
|
||||
adb shell /system/bin/asanwrapper /path/to/binary
|
||||
```
|
||||
|
||||
Use `build/android/asan_symbolize.py` to symbolize stack from `adb logcat`. It
|
||||
needs the `--output-directory` argument and takes care of translating the device
|
||||
path to the unstripped binary in the output directory.
|
||||
|
@ -3,6 +3,7 @@
|
||||
# found in the LICENSE file.
|
||||
|
||||
import("//base/allocator/partition_allocator/partition_alloc.gni")
|
||||
import("//build/config/sanitizers/sanitizers.gni")
|
||||
|
||||
# Intermediate target grouping the android tools needed to run native
|
||||
# unittests and instrumentation test apks.
|
||||
@ -18,6 +19,9 @@ group("android_tools") {
|
||||
"//tools/perf:run_benchmark_wrapper",
|
||||
"//tools/perf/clear_system_cache",
|
||||
]
|
||||
if (is_asan) {
|
||||
deps += [ "//tools/android/asan/third_party:asan_device_setup" ]
|
||||
}
|
||||
if (use_full_mte) {
|
||||
deps += [ "//tools/android/mte:mte_device_setup" ]
|
||||
}
|
||||
|
54
tools/android/asan/third_party/BUILD.gn
vendored
Normal file
54
tools/android/asan/third_party/BUILD.gn
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
# Copyright 2019 The Chromium Authors
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
import("//build/config/android/config.gni")
|
||||
import("//build/config/clang/clang.gni")
|
||||
import("//build/util/generate_wrapper.gni")
|
||||
|
||||
generate_wrapper("asan_device_setup") {
|
||||
testonly = true
|
||||
executable = "with_asan.py"
|
||||
wrapper_script = "$root_out_dir/bin/run_with_asan"
|
||||
|
||||
_lib_archs = []
|
||||
|
||||
if (target_cpu == "arm" || target_cpu == "arm64") {
|
||||
_lib_archs += [
|
||||
"arm",
|
||||
"aarch64",
|
||||
]
|
||||
} else if (target_cpu == "x86") {
|
||||
_lib_archs += [ "i686" ]
|
||||
} else {
|
||||
assert(false, "No ASAN library available for $target_cpu")
|
||||
}
|
||||
|
||||
_adb_path = "${public_android_sdk_root}/platform-tools/adb"
|
||||
_lib_dir = "${clang_base_path}/lib/clang/${clang_version}/lib/linux"
|
||||
_lib_paths = []
|
||||
foreach(_lib_arch, _lib_archs) {
|
||||
_lib_paths += [ "${_lib_dir}/libclang_rt.asan-${_lib_arch}-android.so" ]
|
||||
}
|
||||
data = [
|
||||
"asan_device_setup.sh",
|
||||
"with_asan.py",
|
||||
_adb_path,
|
||||
]
|
||||
data += _lib_paths
|
||||
|
||||
data_deps = [
|
||||
"//build/android:devil_chromium_py",
|
||||
"//third_party/catapult/devil",
|
||||
]
|
||||
|
||||
_rebased_adb_path = rebase_path(_adb_path, root_build_dir)
|
||||
_rebased_lib_dir_path = rebase_path(_lib_dir, root_build_dir)
|
||||
|
||||
executable_args = [
|
||||
"--adb",
|
||||
"@WrappedPath(${_rebased_adb_path})",
|
||||
"--lib",
|
||||
"@WrappedPath(${_rebased_lib_dir_path})",
|
||||
]
|
||||
}
|
8
tools/android/asan/third_party/README.chromium
vendored
Normal file
8
tools/android/asan/third_party/README.chromium
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
Name: asan_device_setup.sh
|
||||
License: Apache 2.0
|
||||
Version: fbb9132e71a2
|
||||
URL: https://reviews.llvm.org/source/llvm-github/browse/main/compiler-rt/lib/asan/scripts/asan_device_setup
|
||||
Security Critical: no
|
||||
Shipped: no
|
||||
|
||||
asan_device_setup.sh is a verbatim copy of asan_device_setup in the LLVM trunk.
|
466
tools/android/asan/third_party/asan_device_setup.sh
vendored
Executable file
466
tools/android/asan/third_party/asan_device_setup.sh
vendored
Executable file
@ -0,0 +1,466 @@
|
||||
#!/bin/bash
|
||||
#===- lib/asan/scripts/asan_device_setup -----------------------------------===#
|
||||
#
|
||||
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
# See https://llvm.org/LICENSE.txt for license information.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
#
|
||||
# Prepare Android device to run ASan applications.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
set -e
|
||||
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
revert=no
|
||||
extra_options=
|
||||
device=
|
||||
lib=
|
||||
use_su=0
|
||||
|
||||
function usage {
|
||||
echo "usage: $0 [--revert] [--device device-id] [--lib path] [--extra-options options]"
|
||||
echo " --revert: Uninstall ASan from the device."
|
||||
echo " --lib: Path to ASan runtime library."
|
||||
echo " --extra-options: Extra ASAN_OPTIONS."
|
||||
echo " --device: Install to the given device. Use 'adb devices' to find"
|
||||
echo " device-id."
|
||||
echo " --use-su: Use 'su -c' prefix for every adb command instead of using"
|
||||
echo " 'adb root' once."
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
function adb_push {
|
||||
if [ $use_su -eq 0 ]; then
|
||||
$ADB push "$1" "$2"
|
||||
else
|
||||
local FILENAME=$(basename $1)
|
||||
$ADB push "$1" "/data/local/tmp/$FILENAME"
|
||||
$ADB shell su -c "rm \\\"$2/$FILENAME\\\"" >&/dev/null
|
||||
$ADB shell su -c "cat \\\"/data/local/tmp/$FILENAME\\\" > \\\"$2/$FILENAME\\\""
|
||||
$ADB shell su -c "rm \\\"/data/local/tmp/$FILENAME\\\""
|
||||
fi
|
||||
}
|
||||
|
||||
function adb_remount {
|
||||
if [ $use_su -eq 0 ]; then
|
||||
$ADB remount
|
||||
else
|
||||
local STORAGE=`$ADB shell mount | grep /system | cut -d ' ' -f1`
|
||||
if [ "$STORAGE" != "" ]; then
|
||||
echo Remounting $STORAGE at /system
|
||||
$ADB shell su -c "mount -o rw,remount $STORAGE /system"
|
||||
else
|
||||
echo Failed to get storage device name for "/system" mount point
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function adb_shell {
|
||||
if [ $use_su -eq 0 ]; then
|
||||
$ADB shell $@
|
||||
else
|
||||
$ADB shell su -c "$*"
|
||||
fi
|
||||
}
|
||||
|
||||
function adb_root {
|
||||
if [ $use_su -eq 0 ]; then
|
||||
$ADB root
|
||||
fi
|
||||
}
|
||||
|
||||
function adb_wait_for_device {
|
||||
$ADB wait-for-device
|
||||
}
|
||||
|
||||
function adb_pull {
|
||||
if [ $use_su -eq 0 ]; then
|
||||
$ADB pull "$1" "$2"
|
||||
else
|
||||
local FILENAME=$(basename $1)
|
||||
$ADB shell rm "/data/local/tmp/$FILENAME" >&/dev/null
|
||||
$ADB shell su -c "[ -f \\\"$1\\\" ] && cat \\\"$1\\\" > \\\"/data/local/tmp/$FILENAME\\\" && chown root.shell \\\"/data/local/tmp/$FILENAME\\\" && chmod 755 \\\"/data/local/tmp/$FILENAME\\\"" &&
|
||||
$ADB pull "/data/local/tmp/$FILENAME" "$2" >&/dev/null && $ADB shell "rm \"/data/local/tmp/$FILENAME\""
|
||||
fi
|
||||
}
|
||||
|
||||
function get_device_arch { # OUT OUT64
|
||||
local _outvar=$1
|
||||
local _outvar64=$2
|
||||
local _ABI=$(adb_shell getprop ro.product.cpu.abi)
|
||||
local _ARCH=
|
||||
local _ARCH64=
|
||||
if [[ $_ABI == x86* ]]; then
|
||||
_ARCH=i686
|
||||
elif [[ $_ABI == armeabi* ]]; then
|
||||
_ARCH=arm
|
||||
elif [[ $_ABI == arm64-v8a* ]]; then
|
||||
_ARCH=arm
|
||||
_ARCH64=aarch64
|
||||
else
|
||||
echo "Unrecognized device ABI: $_ABI"
|
||||
exit 1
|
||||
fi
|
||||
eval $_outvar=\$_ARCH
|
||||
eval $_outvar64=\$_ARCH64
|
||||
}
|
||||
|
||||
while [[ $# > 0 ]]; do
|
||||
case $1 in
|
||||
--revert)
|
||||
revert=yes
|
||||
;;
|
||||
--extra-options)
|
||||
shift
|
||||
if [[ $# == 0 ]]; then
|
||||
echo "--extra-options requires an argument."
|
||||
exit 1
|
||||
fi
|
||||
extra_options="$1"
|
||||
;;
|
||||
--lib)
|
||||
shift
|
||||
if [[ $# == 0 ]]; then
|
||||
echo "--lib requires an argument."
|
||||
exit 1
|
||||
fi
|
||||
lib="$1"
|
||||
;;
|
||||
--device)
|
||||
shift
|
||||
if [[ $# == 0 ]]; then
|
||||
echo "--device requires an argument."
|
||||
exit 1
|
||||
fi
|
||||
device="$1"
|
||||
;;
|
||||
--use-su)
|
||||
use_su=1
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
ADB=${ADB:-adb}
|
||||
if [[ x$device != x ]]; then
|
||||
ADB="$ADB -s $device"
|
||||
fi
|
||||
|
||||
if [ $use_su -eq 1 ]; then
|
||||
# Test if 'su' is present on the device
|
||||
SU_TEST_OUT=`$ADB shell su -c "echo foo" 2>&1 | sed 's/\r$//'`
|
||||
if [ $? != 0 -o "$SU_TEST_OUT" != "foo" ]; then
|
||||
echo "ERROR: Cannot use 'su -c':"
|
||||
echo "$ adb shell su -c \"echo foo\""
|
||||
echo $SU_TEST_OUT
|
||||
echo "Check that 'su' binary is correctly installed on the device or omit"
|
||||
echo " --use-su flag"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo '>> Remounting /system rw'
|
||||
adb_wait_for_device
|
||||
adb_root
|
||||
adb_wait_for_device
|
||||
adb_remount
|
||||
adb_wait_for_device
|
||||
|
||||
get_device_arch ARCH ARCH64
|
||||
echo "Target architecture: $ARCH"
|
||||
ASAN_RT="libclang_rt.asan-$ARCH-android.so"
|
||||
if [[ -n $ARCH64 ]]; then
|
||||
echo "Target architecture: $ARCH64"
|
||||
ASAN_RT64="libclang_rt.asan-$ARCH64-android.so"
|
||||
fi
|
||||
|
||||
RELEASE=$(adb_shell getprop ro.build.version.release)
|
||||
PRE_L=0
|
||||
if echo "$RELEASE" | grep '^4\.' >&/dev/null; then
|
||||
PRE_L=1
|
||||
fi
|
||||
ANDROID_O=0
|
||||
if echo "$RELEASE" | grep '^8\.0\.' >&/dev/null; then
|
||||
# 8.0.x is for Android O
|
||||
ANDROID_O=1
|
||||
fi
|
||||
|
||||
if [[ x$revert == xyes ]]; then
|
||||
echo '>> Uninstalling ASan'
|
||||
|
||||
if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then
|
||||
echo '>> Pre-L device detected.'
|
||||
adb_shell mv /system/bin/app_process.real /system/bin/app_process
|
||||
adb_shell rm /system/bin/asanwrapper
|
||||
elif ! adb_shell ls -l /system/bin/app_process64.real | grep -o 'No such file or directory' >&/dev/null; then
|
||||
# 64-bit installation.
|
||||
adb_shell mv /system/bin/app_process32.real /system/bin/app_process32
|
||||
adb_shell mv /system/bin/app_process64.real /system/bin/app_process64
|
||||
adb_shell rm /system/bin/asanwrapper
|
||||
adb_shell rm /system/bin/asanwrapper64
|
||||
else
|
||||
# 32-bit installation.
|
||||
adb_shell rm /system/bin/app_process.wrap
|
||||
adb_shell rm /system/bin/asanwrapper
|
||||
adb_shell rm /system/bin/app_process
|
||||
adb_shell ln -s /system/bin/app_process32 /system/bin/app_process
|
||||
fi
|
||||
|
||||
if [[ ANDROID_O -eq 1 ]]; then
|
||||
adb_shell mv /system/etc/ld.config.txt.saved /system/etc/ld.config.txt
|
||||
fi
|
||||
|
||||
echo '>> Restarting shell'
|
||||
adb_shell stop
|
||||
adb_shell start
|
||||
|
||||
# Remove the library on the last step to give a chance to the 'su' binary to
|
||||
# be executed without problem.
|
||||
adb_shell rm /system/lib/$ASAN_RT
|
||||
|
||||
echo '>> Done'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ -d "$lib" ]]; then
|
||||
ASAN_RT_PATH="$lib"
|
||||
elif [[ -f "$lib" && "$lib" == *"$ASAN_RT" ]]; then
|
||||
ASAN_RT_PATH=$(dirname "$lib")
|
||||
elif [[ -f "$HERE/$ASAN_RT" ]]; then
|
||||
ASAN_RT_PATH="$HERE"
|
||||
elif [[ $(basename "$HERE") == "bin" ]]; then
|
||||
# We could be in the toolchain's base directory.
|
||||
# Consider ../lib, ../lib/asan, ../lib/linux,
|
||||
# ../lib/clang/$VERSION/lib/linux, and ../lib64/clang/$VERSION/lib/linux.
|
||||
P=$(ls "$HERE"/../lib/"$ASAN_RT" \
|
||||
"$HERE"/../lib/asan/"$ASAN_RT" \
|
||||
"$HERE"/../lib/linux/"$ASAN_RT" \
|
||||
"$HERE"/../lib/clang/*/lib/linux/"$ASAN_RT" \
|
||||
"$HERE"/../lib64/clang/*/lib/linux/"$ASAN_RT" 2>/dev/null | sort | tail -1)
|
||||
if [[ -n "$P" ]]; then
|
||||
ASAN_RT_PATH="$(dirname "$P")"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$ASAN_RT_PATH" || ! -f "$ASAN_RT_PATH/$ASAN_RT" ]]; then
|
||||
echo ">> ASan runtime library not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "$ASAN_RT64" ]]; then
|
||||
if [[ -z "$ASAN_RT_PATH" || ! -f "$ASAN_RT_PATH/$ASAN_RT64" ]]; then
|
||||
echo ">> ASan runtime library not found"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
TMPDIRBASE=$(mktemp -d)
|
||||
TMPDIROLD="$TMPDIRBASE/old"
|
||||
TMPDIR="$TMPDIRBASE/new"
|
||||
mkdir "$TMPDIROLD"
|
||||
|
||||
if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then
|
||||
|
||||
if adb_pull /system/bin/app_process.real /dev/null >&/dev/null; then
|
||||
echo '>> Old-style ASan installation detected. Reverting.'
|
||||
adb_shell mv /system/bin/app_process.real /system/bin/app_process
|
||||
fi
|
||||
|
||||
echo '>> Pre-L device detected. Setting up app_process symlink.'
|
||||
adb_shell mv /system/bin/app_process /system/bin/app_process32
|
||||
adb_shell ln -s /system/bin/app_process32 /system/bin/app_process
|
||||
fi
|
||||
|
||||
echo '>> Copying files from the device'
|
||||
if [[ -n "$ASAN_RT64" ]]; then
|
||||
adb_pull /system/lib/"$ASAN_RT" "$TMPDIROLD" || true
|
||||
adb_pull /system/lib64/"$ASAN_RT64" "$TMPDIROLD" || true
|
||||
adb_pull /system/bin/app_process32 "$TMPDIROLD" || true
|
||||
adb_pull /system/bin/app_process32.real "$TMPDIROLD" || true
|
||||
adb_pull /system/bin/app_process64 "$TMPDIROLD" || true
|
||||
adb_pull /system/bin/app_process64.real "$TMPDIROLD" || true
|
||||
adb_pull /system/bin/asanwrapper "$TMPDIROLD" || true
|
||||
adb_pull /system/bin/asanwrapper64 "$TMPDIROLD" || true
|
||||
else
|
||||
adb_pull /system/lib/"$ASAN_RT" "$TMPDIROLD" || true
|
||||
adb_pull /system/bin/app_process32 "$TMPDIROLD" || true
|
||||
adb_pull /system/bin/app_process.wrap "$TMPDIROLD" || true
|
||||
adb_pull /system/bin/asanwrapper "$TMPDIROLD" || true
|
||||
fi
|
||||
cp -r "$TMPDIROLD" "$TMPDIR"
|
||||
|
||||
if [[ -f "$TMPDIR/app_process.wrap" || -f "$TMPDIR/app_process64.real" ]]; then
|
||||
echo ">> Previous installation detected"
|
||||
else
|
||||
echo ">> New installation"
|
||||
fi
|
||||
|
||||
echo '>> Generating wrappers'
|
||||
|
||||
cp "$ASAN_RT_PATH/$ASAN_RT" "$TMPDIR/"
|
||||
if [[ -n "$ASAN_RT64" ]]; then
|
||||
cp "$ASAN_RT_PATH/$ASAN_RT64" "$TMPDIR/"
|
||||
fi
|
||||
|
||||
ASAN_OPTIONS=start_deactivated=1
|
||||
|
||||
# The name of a symlink to libclang_rt.asan-$ARCH-android.so used in LD_PRELOAD.
|
||||
# The idea is to have the same name in lib and lib64 to keep it from falling
|
||||
# apart when a 64-bit process spawns a 32-bit one, inheriting the environment.
|
||||
ASAN_RT_SYMLINK=symlink-to-libclang_rt.asan
|
||||
|
||||
function generate_zygote_wrapper { # from, to
|
||||
local _from=$1
|
||||
local _to=$2
|
||||
if [[ PRE_L -eq 0 ]]; then
|
||||
# LD_PRELOAD parsing is broken in N if it starts with ":". Luckily, it is
|
||||
# unset in the system environment since L.
|
||||
local _ld_preload=$ASAN_RT_SYMLINK
|
||||
else
|
||||
local _ld_preload=\$LD_PRELOAD:$ASAN_RT_SYMLINK
|
||||
fi
|
||||
cat <<EOF >"$TMPDIR/$_from"
|
||||
#!/system/bin/sh-from-zygote
|
||||
ASAN_OPTIONS=$ASAN_OPTIONS \\
|
||||
ASAN_ACTIVATION_OPTIONS=include_if_exists=/data/local/tmp/asan.options.%b \\
|
||||
LD_PRELOAD=$_ld_preload \\
|
||||
exec $_to "\$@"
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
# On Android-L not allowing user segv handler breaks some applications.
|
||||
# Since ~May 2017 this is the default setting; included for compatibility with
|
||||
# older library versions.
|
||||
if [[ PRE_L -eq 0 ]]; then
|
||||
ASAN_OPTIONS="$ASAN_OPTIONS,allow_user_segv_handler=1"
|
||||
fi
|
||||
|
||||
if [[ x$extra_options != x ]] ; then
|
||||
ASAN_OPTIONS="$ASAN_OPTIONS,$extra_options"
|
||||
fi
|
||||
|
||||
# Zygote wrapper.
|
||||
if [[ -f "$TMPDIR/app_process64" ]]; then
|
||||
# A 64-bit device.
|
||||
if [[ ! -f "$TMPDIR/app_process64.real" ]]; then
|
||||
# New installation.
|
||||
mv "$TMPDIR/app_process32" "$TMPDIR/app_process32.real"
|
||||
mv "$TMPDIR/app_process64" "$TMPDIR/app_process64.real"
|
||||
fi
|
||||
generate_zygote_wrapper "app_process32" "/system/bin/app_process32.real"
|
||||
generate_zygote_wrapper "app_process64" "/system/bin/app_process64.real"
|
||||
else
|
||||
# A 32-bit device.
|
||||
generate_zygote_wrapper "app_process.wrap" "/system/bin/app_process32"
|
||||
fi
|
||||
|
||||
# General command-line tool wrapper (use for anything that's not started as
|
||||
# zygote).
|
||||
cat <<EOF >"$TMPDIR/asanwrapper"
|
||||
#!/system/bin/sh
|
||||
LD_PRELOAD=$ASAN_RT_SYMLINK \\
|
||||
exec \$@
|
||||
|
||||
EOF
|
||||
|
||||
if [[ -n "$ASAN_RT64" ]]; then
|
||||
cat <<EOF >"$TMPDIR/asanwrapper64"
|
||||
#!/system/bin/sh
|
||||
LD_PRELOAD=$ASAN_RT_SYMLINK \\
|
||||
exec \$@
|
||||
|
||||
EOF
|
||||
fi
|
||||
|
||||
function install { # from, to, chmod, chcon
|
||||
local _from=$1
|
||||
local _to=$2
|
||||
local _mode=$3
|
||||
local _context=$4
|
||||
local _basename="$(basename "$_from")"
|
||||
echo "Installing $_to/$_basename $_mode $_context"
|
||||
adb_push "$_from" "$_to/$_basename"
|
||||
adb_shell chown root.shell "$_to/$_basename"
|
||||
if [[ -n "$_mode" ]]; then
|
||||
adb_shell chmod "$_mode" "$_to/$_basename"
|
||||
fi
|
||||
if [[ -n "$_context" ]]; then
|
||||
adb_shell chcon "$_context" "$_to/$_basename"
|
||||
fi
|
||||
}
|
||||
|
||||
if ! ( cd "$TMPDIRBASE" && diff -qr old/ new/ ) ; then
|
||||
# Make SELinux happy by keeping app_process wrapper and the shell
|
||||
# it runs on in zygote domain.
|
||||
ENFORCING=0
|
||||
if adb_shell getenforce | grep Enforcing >/dev/null; then
|
||||
# Sometimes shell is not allowed to change file contexts.
|
||||
# Temporarily switch to permissive.
|
||||
ENFORCING=1
|
||||
adb_shell setenforce 0
|
||||
fi
|
||||
|
||||
if [[ PRE_L -eq 1 ]]; then
|
||||
CTX=u:object_r:system_file:s0
|
||||
else
|
||||
CTX=u:object_r:zygote_exec:s0
|
||||
fi
|
||||
|
||||
echo '>> Pushing files to the device'
|
||||
|
||||
if [[ -n "$ASAN_RT64" ]]; then
|
||||
install "$TMPDIR/$ASAN_RT" /system/lib 644
|
||||
install "$TMPDIR/$ASAN_RT64" /system/lib64 644
|
||||
install "$TMPDIR/app_process32" /system/bin 755 $CTX
|
||||
install "$TMPDIR/app_process32.real" /system/bin 755 $CTX
|
||||
install "$TMPDIR/app_process64" /system/bin 755 $CTX
|
||||
install "$TMPDIR/app_process64.real" /system/bin 755 $CTX
|
||||
install "$TMPDIR/asanwrapper" /system/bin 755
|
||||
install "$TMPDIR/asanwrapper64" /system/bin 755
|
||||
|
||||
adb_shell rm -f /system/lib/$ASAN_RT_SYMLINK
|
||||
adb_shell ln -s $ASAN_RT /system/lib/$ASAN_RT_SYMLINK
|
||||
adb_shell rm -f /system/lib64/$ASAN_RT_SYMLINK
|
||||
adb_shell ln -s $ASAN_RT64 /system/lib64/$ASAN_RT_SYMLINK
|
||||
else
|
||||
install "$TMPDIR/$ASAN_RT" /system/lib 644
|
||||
install "$TMPDIR/app_process32" /system/bin 755 $CTX
|
||||
install "$TMPDIR/app_process.wrap" /system/bin 755 $CTX
|
||||
install "$TMPDIR/asanwrapper" /system/bin 755 $CTX
|
||||
|
||||
adb_shell rm -f /system/lib/$ASAN_RT_SYMLINK
|
||||
adb_shell ln -s $ASAN_RT /system/lib/$ASAN_RT_SYMLINK
|
||||
|
||||
adb_shell rm /system/bin/app_process
|
||||
adb_shell ln -s /system/bin/app_process.wrap /system/bin/app_process
|
||||
fi
|
||||
|
||||
adb_shell cp /system/bin/sh /system/bin/sh-from-zygote
|
||||
adb_shell chcon $CTX /system/bin/sh-from-zygote
|
||||
|
||||
if [[ ANDROID_O -eq 1 ]]; then
|
||||
# For Android O, the linker namespace is temporarily disabled.
|
||||
adb_shell mv /system/etc/ld.config.txt /system/etc/ld.config.txt.saved
|
||||
fi
|
||||
|
||||
if [ $ENFORCING == 1 ]; then
|
||||
adb_shell setenforce 1
|
||||
fi
|
||||
|
||||
echo '>> Restarting shell (asynchronous)'
|
||||
adb_shell stop
|
||||
adb_shell start
|
||||
|
||||
echo '>> Please wait until the device restarts'
|
||||
else
|
||||
echo '>> Device is up to date'
|
||||
fi
|
||||
|
||||
rm -r "$TMPDIRBASE"
|
127
tools/android/asan/third_party/with_asan.py
vendored
Executable file
127
tools/android/asan/third_party/with_asan.py
vendored
Executable file
@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env vpython3
|
||||
# Copyright 2019 The Chromium Authors
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
|
||||
import argparse
|
||||
import contextlib
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
_SRC_ROOT = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), '..', '..', '..', '..'))
|
||||
|
||||
sys.path.append(os.path.join(_SRC_ROOT, 'third_party', 'catapult', 'devil'))
|
||||
from devil import base_error
|
||||
from devil.android import device_utils
|
||||
from devil.android.sdk import adb_wrapper
|
||||
from devil.android.sdk import version_codes
|
||||
from devil.utils import logging_common
|
||||
|
||||
sys.path.append(os.path.join(_SRC_ROOT, 'build', 'android'))
|
||||
import devil_chromium
|
||||
|
||||
_SCRIPT_PATH = os.path.abspath(
|
||||
os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
'asan_device_setup.sh'))
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _LogDevicesOnFailure(msg):
|
||||
try:
|
||||
yield
|
||||
except base_error.BaseError:
|
||||
logging.exception(msg)
|
||||
logging.error('Devices visible to adb:')
|
||||
for entry in adb_wrapper.AdbWrapper.Devices(desired_state=None,
|
||||
long_list=True):
|
||||
logging.error(' %s: %s',
|
||||
entry[0].GetDeviceSerial(),
|
||||
' '.join(entry[1:]))
|
||||
raise
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def Asan(args):
|
||||
env = os.environ.copy()
|
||||
env['ADB'] = args.adb
|
||||
|
||||
try:
|
||||
with _LogDevicesOnFailure('Failed to set up the device.'):
|
||||
device = device_utils.DeviceUtils.HealthyDevices(
|
||||
device_arg=args.device)[0]
|
||||
disable_verity = device.build_version_sdk >= version_codes.MARSHMALLOW
|
||||
if disable_verity:
|
||||
device.EnableRoot()
|
||||
# TODO(crbug.com/790202): Stop logging output after diagnosing
|
||||
# issues on android-asan.
|
||||
verity_output = device.adb.DisableVerity()
|
||||
if verity_output:
|
||||
logging.info('disable-verity output:')
|
||||
for line in verity_output.splitlines():
|
||||
logging.info(' %s', line)
|
||||
device.Reboot()
|
||||
# Call EnableRoot prior to asan_device_setup.sh to ensure it doesn't
|
||||
# get tripped up by the root timeout.
|
||||
device.EnableRoot()
|
||||
setup_cmd = [_SCRIPT_PATH, '--lib', args.lib]
|
||||
if args.device:
|
||||
setup_cmd += ['--device', args.device]
|
||||
subprocess.check_call(setup_cmd, env=env)
|
||||
yield
|
||||
finally:
|
||||
with _LogDevicesOnFailure('Failed to tear down the device.'):
|
||||
device.EnableRoot()
|
||||
teardown_cmd = [_SCRIPT_PATH, '--revert']
|
||||
if args.device:
|
||||
teardown_cmd += ['--device', args.device]
|
||||
subprocess.check_call(teardown_cmd, env=env)
|
||||
if disable_verity:
|
||||
# TODO(crbug.com/790202): Stop logging output after diagnosing
|
||||
# issues on android-asan.
|
||||
verity_output = device.adb.EnableVerity()
|
||||
if verity_output:
|
||||
logging.info('enable-verity output:')
|
||||
for line in verity_output.splitlines():
|
||||
logging.info(' %s', line)
|
||||
device.Reboot()
|
||||
|
||||
|
||||
def main(raw_args):
|
||||
parser = argparse.ArgumentParser()
|
||||
logging_common.AddLoggingArguments(parser)
|
||||
parser.add_argument(
|
||||
'--adb', type=os.path.realpath, required=True,
|
||||
help='Path to adb binary.')
|
||||
parser.add_argument(
|
||||
'--device',
|
||||
help='Device serial.')
|
||||
parser.add_argument(
|
||||
'--lib', type=os.path.realpath, required=True,
|
||||
help='Path to asan library.')
|
||||
parser.add_argument(
|
||||
'command', nargs='*',
|
||||
help='Command to run with ASAN installed.')
|
||||
args = parser.parse_args()
|
||||
|
||||
# TODO(crbug.com/790202): Remove this after diagnosing issues
|
||||
# with android-asan.
|
||||
if not args.quiet:
|
||||
args.verbose += 1
|
||||
|
||||
logging_common.InitializeLogging(args)
|
||||
devil_chromium.Initialize(adb_path=args.adb)
|
||||
|
||||
with Asan(args):
|
||||
if args.command:
|
||||
return subprocess.call(args.command)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
Reference in New Issue
Block a user