From aa9d74cc5db8c2a16c2cd39bfb7e501ca1bb7c72 Mon Sep 17 00:00:00 2001 From: Evgeniy Stepanov Date: Fri, 14 Feb 2014 09:22:10 +0000 Subject: [PATCH] [asan] Android test runner for ASan lit tests. This change replaces 32- and 64- bit config.in-s with a single config template that is used to generate both 32 and 64 bits configs as well as the new arm-android config. Arm-android config is special because it can run tests on a remote device over adb (android debug bridge). We replace %clang with a script that run the compiler, upload the result to the device, and replaces it with another script. The second script runs the binary on the device and delivers stdout/stderr/exitcode back. llvm-svn: 201394 --- compiler-rt/CMakeLists.txt | 41 ++++++++------- .../lib/asan/lit_tests/64bitConfig/lit.site.cfg.in | 12 ----- compiler-rt/lib/asan/lit_tests/CMakeLists.txt | 59 +++++++++++++++------- .../{32bitConfig => GenericConfig}/lit.site.cfg.in | 14 +++-- .../lit_tests/android_commands/android_common.py | 29 +++++++++++ .../lit_tests/android_commands/android_compile.py | 36 +++++++++++++ .../asan/lit_tests/android_commands/android_run.py | 34 +++++++++++++ compiler-rt/lib/asan/lit_tests/lit.cfg | 25 ++++++--- compiler-rt/lib/lit.common.configured.in | 29 ++++++----- 9 files changed, 207 insertions(+), 72 deletions(-) delete mode 100644 compiler-rt/lib/asan/lit_tests/64bitConfig/lit.site.cfg.in rename compiler-rt/lib/asan/lit_tests/{32bitConfig => GenericConfig}/lit.site.cfg.in (60%) create mode 100644 compiler-rt/lib/asan/lit_tests/android_commands/android_common.py create mode 100755 compiler-rt/lib/asan/lit_tests/android_commands/android_compile.py create mode 100755 compiler-rt/lib/asan/lit_tests/android_commands/android_run.py diff --git a/compiler-rt/CMakeLists.txt b/compiler-rt/CMakeLists.txt index 8f00eea..55fe62f 100644 --- a/compiler-rt/CMakeLists.txt +++ b/compiler-rt/CMakeLists.txt @@ -99,21 +99,25 @@ macro(test_target_arch arch) endif() endmacro() -if("${LLVM_NATIVE_ARCH}" STREQUAL "X86") - if (NOT MSVC) - test_target_arch(x86_64 ${TARGET_64_BIT_CFLAGS}) +if(ANDROID_COMMON_FLAGS) + test_target_arch(arm_android "${ANDROID_COMMON_FLAGS}") +else() + if("${LLVM_NATIVE_ARCH}" STREQUAL "X86") + if (NOT MSVC) + test_target_arch(x86_64 ${TARGET_64_BIT_CFLAGS}) + endif() + test_target_arch(i386 ${TARGET_32_BIT_CFLAGS}) + elseif("${LLVM_NATIVE_ARCH}" STREQUAL "PowerPC") + test_target_arch(powerpc64 ${TARGET_64_BIT_CFLAGS}) + elseif("${LLVM_NATIVE_ARCH}" STREQUAL "ARM") + test_target_arch(arm "") endif() - test_target_arch(i386 ${TARGET_32_BIT_CFLAGS}) -elseif("${LLVM_NATIVE_ARCH}" STREQUAL "PowerPC") - test_target_arch(powerpc64 ${TARGET_64_BIT_CFLAGS}) -elseif("${LLVM_NATIVE_ARCH}" STREQUAL "ARM") - test_target_arch(arm "") endif() # We only support running instrumented tests when we're not cross compiling -# and target a unix-like system. On Android we define the rules for building -# unit tests, but don't execute them. -if("${CMAKE_HOST_SYSTEM}" STREQUAL "${CMAKE_SYSTEM}" AND UNIX AND NOT ANDROID) +# and target a unix-like system. We can run tests on Android even when we are +# cross-compiling. +if(("${CMAKE_HOST_SYSTEM}" STREQUAL "${CMAKE_SYSTEM}" AND UNIX) OR ANDROID) option(COMPILER_RT_CAN_EXECUTE_TESTS "Can we execute instrumented tests" ON) else() option(COMPILER_RT_CAN_EXECUTE_TESTS "Can we execute instrumented tests" OFF) @@ -230,12 +234,15 @@ filter_available_targets(SANITIZER_COMMON_SUPPORTED_ARCH add_subdirectory(include) -set(SANITIZER_COMMON_LIT_TEST_DEPS - clang clang-headers FileCheck count not llvm-nm llvm-symbolizer - compiler-rt-headers) -# Check code style when running lit tests for sanitizers. -if(UNIX) - list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS SanitizerLintCheck) +# When ANDROID, we build tests with the host compiler (i.e. CMAKE_C_COMPILER), +# and run tests with tools from the host toolchain. +if (NOT ANDROID) + set(SANITIZER_COMMON_LIT_TEST_DEPS + clang clang-headers FileCheck count not llvm-nm llvm-symbolizer + compiler-rt-headers) + if(UNIX) + list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS SanitizerLintCheck) + endif() endif() add_subdirectory(lib) diff --git a/compiler-rt/lib/asan/lit_tests/64bitConfig/lit.site.cfg.in b/compiler-rt/lib/asan/lit_tests/64bitConfig/lit.site.cfg.in deleted file mode 100644 index a359944..0000000 --- a/compiler-rt/lib/asan/lit_tests/64bitConfig/lit.site.cfg.in +++ /dev/null @@ -1,12 +0,0 @@ -## Autogenerated by LLVM/Clang configuration. -# Do not edit! - -# Load common config for all compiler-rt lit tests. -lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") - -# Tool-specific config options. -config.asan_source_dir = "@ASAN_SOURCE_DIR@" -config.bits = "64" - -# Load tool-specific config that would do the real work. -lit_config.load_config(config, "@ASAN_SOURCE_DIR@/lit_tests/lit.cfg") diff --git a/compiler-rt/lib/asan/lit_tests/CMakeLists.txt b/compiler-rt/lib/asan/lit_tests/CMakeLists.txt index 72a3f54..223052a 100644 --- a/compiler-rt/lib/asan/lit_tests/CMakeLists.txt +++ b/compiler-rt/lib/asan/lit_tests/CMakeLists.txt @@ -1,36 +1,59 @@ set(ASAN_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..) set(ASAN_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/..) -configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/64bitConfig/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig/lit.site.cfg - ) - -configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/32bitConfig/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig/lit.site.cfg - ) - -configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg - ) - if(COMPILER_RT_CAN_EXECUTE_TESTS) set(ASAN_TESTSUITES) - if(CAN_TARGET_i386) - list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig) + + if(CAN_TARGET_arm_android) + # This is only true if we are cross-compiling. + # Build all tests with host compiler and use host tools. + set(ASAN_TEST_TARGET_CC ${CMAKE_C_COMPILER}) + get_filename_component(ASAN_TEST_LLVM_TOOLS_DIR ${CMAKE_C_COMPILER} PATH) + set(ASAN_TEST_CONFIG_SUFFIX "-arm-android") + set(ASAN_TEST_BITS "32") + get_target_flags_for_arch(arm_android ASAN_TEST_TARGET_CFLAGS) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/GenericConfig/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/ARMAndroidConfig/lit.site.cfg + ) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/ARMAndroidConfig) endif() + if(CAN_TARGET_x86_64 OR CAN_TARGET_powerpc64) + set(ASAN_TEST_CONFIG_SUFFIX "64") + set(ASAN_TEST_BITS "64") + set(ASAN_TEST_TARGET_CFLAGS ${TARGET_64_BIT_CFLAGS}) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/GenericConfig/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig/lit.site.cfg + ) list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig) endif() + + if(CAN_TARGET_i386) + set(ASAN_TEST_CONFIG_SUFFIX "32") + set(ASAN_TEST_BITS "32") + set(ASAN_TEST_TARGET_CFLAGS ${TARGET_32_BIT_CFLAGS}) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/GenericConfig/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig/lit.site.cfg + ) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig) + endif() + + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg + ) + # Run ASan tests only if we're sure we may produce working binaries. set(ASAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS} asan_runtime_libraries) set(ASAN_TEST_PARAMS asan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) - if(LLVM_INCLUDE_TESTS) + # FIXME: support unit test in the android test runner + if(LLVM_INCLUDE_TESTS AND NOT CAN_TARGET_arm_android) list(APPEND ASAN_TEST_DEPS AsanUnitTests) list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/Unit) endif() diff --git a/compiler-rt/lib/asan/lit_tests/32bitConfig/lit.site.cfg.in b/compiler-rt/lib/asan/lit_tests/GenericConfig/lit.site.cfg.in similarity index 60% rename from compiler-rt/lib/asan/lit_tests/32bitConfig/lit.site.cfg.in rename to compiler-rt/lib/asan/lit_tests/GenericConfig/lit.site.cfg.in index faef4e8..7ecf0e5 100644 --- a/compiler-rt/lib/asan/lit_tests/32bitConfig/lit.site.cfg.in +++ b/compiler-rt/lib/asan/lit_tests/GenericConfig/lit.site.cfg.in @@ -1,13 +1,17 @@ ## Autogenerated by LLVM/Clang configuration. # Do not edit! -# Load common config for all compiler-rt lit tests. -lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") - # Tool-specific config options. +config.name_suffix = "@ASAN_TEST_CONFIG_SUFFIX@" config.asan_source_dir = "@ASAN_SOURCE_DIR@" -config.bits = "32" +config.target_cflags = "@ASAN_TEST_TARGET_CFLAGS@" +config.clang = "@ASAN_TEST_TARGET_CC@" +config.llvm_tools_dir = "@ASAN_TEST_LLVM_TOOLS_DIR@" +config.bits = "@ASAN_TEST_BITS@" +config.android = "@CAN_TARGET_arm_android@" + +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") # Load tool-specific config that would do the real work. lit_config.load_config(config, "@ASAN_SOURCE_DIR@/lit_tests/lit.cfg") - diff --git a/compiler-rt/lib/asan/lit_tests/android_commands/android_common.py b/compiler-rt/lib/asan/lit_tests/android_commands/android_common.py new file mode 100644 index 0000000..43ac7b4 --- /dev/null +++ b/compiler-rt/lib/asan/lit_tests/android_commands/android_common.py @@ -0,0 +1,29 @@ +import os, subprocess, tempfile +import time + +ANDROID_TMPDIR = '/data/local/tmp/Output' +ADB = os.environ.get('ADB', 'adb') + +verbose = False +if os.environ.get('ANDROID_RUN_VERBOSE') == '1': + verbose = True + +def adb(args): + if verbose: + print args + devnull = open(os.devnull, 'w') + return subprocess.call([ADB] + args, stdout=devnull, stderr=subprocess.STDOUT) + +def pull_from_device(path): + tmp = tempfile.mktemp() + adb(['pull', path, tmp]) + text = open(tmp, 'r').read() + os.unlink(tmp) + return text + +def push_to_device(path): + # Workaround for https://code.google.com/p/android/issues/detail?id=65857 + dst_path = os.path.join(ANDROID_TMPDIR, os.path.basename(path)) + tmp_path = dst_path + '.push' + adb(['push', path, tmp_path]) + adb(['shell', 'cp "%s" "%s" 2>&1' % (tmp_path, dst_path)]) diff --git a/compiler-rt/lib/asan/lit_tests/android_commands/android_compile.py b/compiler-rt/lib/asan/lit_tests/android_commands/android_compile.py new file mode 100755 index 0000000..4b88088 --- /dev/null +++ b/compiler-rt/lib/asan/lit_tests/android_commands/android_compile.py @@ -0,0 +1,36 @@ +#!/usr/bin/python + +import os, sys, subprocess +from android_common import * + + +here = os.path.abspath(os.path.dirname(sys.argv[0])) +android_run = os.path.join(here, 'android_run.py') + +output = None +output_type = 'executable' + +args = sys.argv[1:] +while args: + arg = args.pop(0) + if arg == '-shared': + output_type = 'shared' + elif arg == '-c': + output_type = 'object' + elif arg == '-o': + output = args.pop(0) + +if output == None: + print "No output file name!" + sys.exit(1) + +ret = subprocess.call(sys.argv[1:]) +if ret != 0: + sys.exit(ret) + +if output_type in ['executable', 'shared']: + push_to_device(output) + +if output_type == 'executable': + os.rename(output, output + '.real') + os.symlink(android_run, output) diff --git a/compiler-rt/lib/asan/lit_tests/android_commands/android_run.py b/compiler-rt/lib/asan/lit_tests/android_commands/android_run.py new file mode 100755 index 0000000..a6ceeb4 --- /dev/null +++ b/compiler-rt/lib/asan/lit_tests/android_commands/android_run.py @@ -0,0 +1,34 @@ +#!/usr/bin/python + +import os, sys, subprocess, tempfile +from android_common import * + +ANDROID_TMPDIR = '/data/local/tmp/Output' + +here = os.path.abspath(os.path.dirname(sys.argv[0])) +device_binary = os.path.join(ANDROID_TMPDIR, os.path.basename(sys.argv[0])) + +def build_env(): + args = [] + # Android linker ignores RPATH. Set LD_LIBRARY_PATH to Output dir. + args.append('LD_LIBRARY_PATH=%s:%s' % + (ANDROID_TMPDIR, os.environ.get('LD_LIBRARY_PATH', ''))) + for (key, value) in os.environ.items(): + if key in ['ASAN_OPTIONS']: + args.append('%s="%s"' % (key, value)) + return ' '.join(args) + +device_env = build_env() +device_args = ' '.join(sys.argv[1:]) # FIXME: escape? +device_stdout = device_binary + '.stdout' +device_stderr = device_binary + '.stderr' +device_exitcode = device_binary + '.exitcode' +ret = adb(['shell', 'cd %s && %s %s %s >%s 2>%s ; echo $? >%s' % + (ANDROID_TMPDIR, device_env, device_binary, device_args, + device_stdout, device_stderr, device_exitcode)]) +if ret != 0: + sys.exit(ret) + +sys.stdout.write(pull_from_device(device_stdout)) +sys.stderr.write(pull_from_device(device_stderr)) +sys.exit(int(pull_from_device(device_exitcode))) diff --git a/compiler-rt/lib/asan/lit_tests/lit.cfg b/compiler-rt/lib/asan/lit_tests/lit.cfg index 71a700c..de18cd6 100644 --- a/compiler-rt/lib/asan/lit_tests/lit.cfg +++ b/compiler-rt/lib/asan/lit_tests/lit.cfg @@ -14,7 +14,7 @@ def get_required_attr(config, attr_name): return attr_value # Setup config name. -config.name = 'AddressSanitizer' + config.bits +config.name = 'AddressSanitizer' + config.name_suffix # Setup source root. config.test_source_root = os.path.dirname(__file__) @@ -53,23 +53,32 @@ if llvm_src_root is None: # Setup default compiler flags used with -fsanitize=address option. # FIXME: Review the set of required flags and check if it can be reduced. -bits_cflag = " -m" + config.bits +target_cflags = " " + config.target_cflags clang_asan_cflags = (" -fsanitize=address" + " -mno-omit-leaf-frame-pointer" + " -fno-omit-frame-pointer" + " -fno-optimize-sibling-calls" + " -g" - + bits_cflag) + + target_cflags) clang_asan_cxxflags = " --driver-mode=g++" + clang_asan_cflags -config.substitutions.append( ("%clang ", " " + config.clang + bits_cflag + " ")) -config.substitutions.append( ("%clangxx ", (" " + config.clang + + +if config.android == "TRUE": + config.available_features.add('android') + clang_wrapper = os.path.join(config.asan_source_dir, "lit_tests", + "android_commands", "android_compile.py") + " " +else: + clang_wrapper = "" + +config.substitutions.append( ("%clang ", " " + clang_wrapper + config.clang + target_cflags + " ")) +config.substitutions.append( ("%clangxx ", (" " + clang_wrapper + config.clang + " --driver-mode=g++" + - bits_cflag + " ")) ) -config.substitutions.append( ("%clang_asan ", (" " + config.clang + " " + + target_cflags + " ")) ) +config.substitutions.append( ("%clang_asan ", (" " + clang_wrapper + config.clang + " " + clang_asan_cflags + " ")) ) -config.substitutions.append( ("%clangxx_asan ", (" " + config.clang + " " + +config.substitutions.append( ("%clangxx_asan ", (" " + clang_wrapper + config.clang + " " + clang_asan_cxxflags + " ")) ) + # Setup path to asan_symbolize.py script. asan_source_dir = get_required_attr(config, "asan_source_dir") asan_symbolize = os.path.join(asan_source_dir, "scripts", "asan_symbolize.py") diff --git a/compiler-rt/lib/lit.common.configured.in b/compiler-rt/lib/lit.common.configured.in index 558655c..bfed0424 100644 --- a/compiler-rt/lib/lit.common.configured.in +++ b/compiler-rt/lib/lit.common.configured.in @@ -1,19 +1,24 @@ ## Autogenerated by LLVM/Clang configuration. # Do not edit! +# Set attribute value if it is unset. +def set_default(attr, value): + if not getattr(config, attr, None): + setattr(config, attr, value) + # Generic config options for all compiler-rt lit tests. -config.target_triple = "@TARGET_TRIPLE@" -config.host_arch = "@HOST_ARCH@" -config.host_os = "@HOST_OS@" -config.llvm_build_mode = "@LLVM_BUILD_MODE@" -config.llvm_src_root = "@LLVM_SOURCE_DIR@" -config.llvm_obj_root = "@LLVM_BINARY_DIR@" -config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" -config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" -config.clang = "@LLVM_BINARY_DIR@/bin/clang" -config.compiler_rt_arch = "@COMPILER_RT_SUPPORTED_ARCH@" -config.python_executable = "@PYTHON_EXECUTABLE@" -config.compiler_rt_debug = @COMPILER_RT_DEBUG_PYBOOL@ +set_default("target_triple", "@TARGET_TRIPLE@") +set_default("host_arch", "@HOST_ARCH@") +set_default("host_os", "@HOST_OS@") +set_default("llvm_build_mode", "@LLVM_BUILD_MODE@") +set_default("llvm_src_root", "@LLVM_SOURCE_DIR@") +set_default("llvm_obj_root", "@LLVM_BINARY_DIR@") +set_default("compiler_rt_src_root", "@COMPILER_RT_SOURCE_DIR@") +set_default("llvm_tools_dir", "@LLVM_TOOLS_DIR@") +set_default("clang", "@LLVM_BINARY_DIR@/bin/clang") +set_default("compiler_rt_arch", "@COMPILER_RT_SUPPORTED_ARCH@") +set_default("python_executable", "@PYTHON_EXECUTABLE@") +set_default("compiler_rt_debug", @COMPILER_RT_DEBUG_PYBOOL@) # LLVM tools dir can be passed in lit parameters, so try to # apply substitution. -- 2.7.4