Index: lib/xray/xray_interface.cc =================================================================== --- lib/xray/xray_interface.cc +++ lib/xray/xray_interface.cc @@ -82,6 +82,14 @@ return 0; } +int __xray_remove_handler() { + if (XRayInitialized.load(std::memory_order_acquire)) { + __xray::XRayPatchedFunction.store(nullptr, std::memory_order_release); + return 1; + } + return 0; +} + std::atomic XRayPatching{false}; using namespace __xray; @@ -104,7 +112,7 @@ return CleanupInvoker{Fn}; } -XRayPatchingStatus __xray_patch() { +XRayPatchingStatus PatchCommon(bool Patching) { if (!XRayInitialized.load(std::memory_order_acquire)) return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized. @@ -131,7 +139,9 @@ int32_t FuncId = 1; static constexpr uint8_t CallOpCode = 0xe8; static constexpr uint16_t MovR10Seq = 0xba41; + static constexpr uint16_t Jmp9Seq = 0x09eb; static constexpr uint8_t JmpOpCode = 0xe9; + static constexpr uint8_t RetOpCode = 0xc3; uint64_t CurFun = 0; for (std::size_t I = 0; I < InstrMap.Entries; I++) { auto Sled = InstrMap.Sleds[I]; @@ -191,12 +201,19 @@ TrampolineOffset); continue; } - *reinterpret_cast(Sled.Address + 2) = FuncId; - *reinterpret_cast(Sled.Address + 6) = CallOpCode; - *reinterpret_cast(Sled.Address + 7) = TrampolineOffset; - std::atomic_store_explicit( - reinterpret_cast *>(Sled.Address), MovR10Seq, - std::memory_order_release); + if (Patching) { + *reinterpret_cast(Sled.Address + 2) = FuncId; + *reinterpret_cast(Sled.Address + 6) = CallOpCode; + *reinterpret_cast(Sled.Address + 7) = TrampolineOffset; + std::atomic_store_explicit( + reinterpret_cast *>(Sled.Address), MovR10Seq, + std::memory_order_release); + } else { + std::atomic_store_explicit( + reinterpret_cast *>(Sled.Address), Jmp9Seq, + std::memory_order_release); + // FIXME: Write out the nops still? + } } if (Sled.Kind == XRayEntryType::EXIT) { @@ -231,15 +248,26 @@ TrampolineOffset); continue; } - *reinterpret_cast(Sled.Address + 2) = FuncId; - *reinterpret_cast(Sled.Address + 6) = JmpOpCode; - *reinterpret_cast(Sled.Address + 7) = TrampolineOffset; - std::atomic_store_explicit( - reinterpret_cast *>(Sled.Address), MovR10Seq, - std::memory_order_release); + if (Patching) { + *reinterpret_cast(Sled.Address + 2) = FuncId; + *reinterpret_cast(Sled.Address + 6) = JmpOpCode; + *reinterpret_cast(Sled.Address + 7) = TrampolineOffset; + std::atomic_store_explicit( + reinterpret_cast *>(Sled.Address), MovR10Seq, + std::memory_order_release); + } else { + std::atomic_store_explicit( + reinterpret_cast *>(Sled.Address), RetOpCode, + std::memory_order_release); + // FIXME: Write out the nops still? + } } } XRayPatching.store(false, std::memory_order_release); PatchingSuccess = true; return XRayPatchingStatus::SUCCESS; } + +XRayPatchingStatus __xray_patch() { return PatchCommon(true); } + +XRayPatchingStatus __xray_unpatch() { return PatchCommon(false); } Index: test/CMakeLists.txt =================================================================== --- test/CMakeLists.txt +++ test/CMakeLists.txt @@ -79,6 +79,9 @@ if(COMPILER_RT_HAS_SCUDO) add_subdirectory(scudo) endif() + if(COMPILER_RT_HAS_XRAY) + add_subdirectory(xray) + endif() endif() if(COMPILER_RT_STANDALONE_BUILD) Index: test/xray/CMakeLists.txt =================================================================== --- /dev/null +++ test/xray/CMakeLists.txt @@ -0,0 +1,37 @@ +set(XRAY_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +set(XRAY_TESTSUITES) + +set(XRAY_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) +if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND XRAY_TEST_DEPS xray) +endif() + +set(XRAY_TEST_ARCH ${XRAY_SUPPORTED_ARCH}) +foreach(arch ${XRAY_TEST_ARCH}) + set(XRAY_TEST_TARGET_ARCH ${arch}) + string(TOLOWER "-${arch}-${OS_NAME}" XRAY_TEST_CONFIG_SUFFIX) + + if(ANDROID OR ${arch} MATCHES "arm|aarch64") + # This is only true if we are cross-compiling. + # Build all tests with host compiler and use host tools. + set(XRAY_TEST_TARGET_CC ${COMPILER_RT_TEST_COMPILER}) + set(XRAY_TEST_TARGET_CFLAGS ${COMPILER_RT_TEST_COMPILER_CFLAGS}) + else() + get_target_flags_for_arch(${arch} XRAY_TEST_TARGET_CFLAGS) + string(REPLACE ";" " " XRAY_TEST_TARGET_CFLAGS "${XRAY_TEST_TARGET_CFLAGS}") + endif() + + string(TOUPPER ${arch} ARCH_UPPER_CASE) + set(CONFIG_NAME ${ARCH_UPPER_CASE}${OS_NAME}Config) + + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg) + list(APPEND XRAY_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}) +endforeach() + +add_lit_testsuite(check-xray "Running the XRay tests" + ${XRAY_TESTSUITES} + DEPENDS ${XRAY_TEST_DEPS}) +set_target_properties(check-xray PROPERTIES FOLDER "Compiler-RT Misc") Index: test/xray/TestCases/Linux/patching-unpatching.cc =================================================================== --- /dev/null +++ test/xray/TestCases/Linux/patching-unpatching.cc @@ -0,0 +1,47 @@ +// Check that we can patch and un-patch on demand, and that logging gets invoked +// appropriately. +// +// RUN: %clangxx_xray -fxray-instrument -std=c++11 %s -o %t +// RUN: XRAY_OPTIONS="patch_premain=false" %run %t 2>&1 | FileCheck %s + +#include "xray/xray_interface.h" + +#include + +bool called = false; + +void test_handler(int32_t fid, XRayEntryType type) { + printf("called: %d, type=%d\n", fid, static_cast(type)); + called = true; +} + +[[clang::xray_always_instrument]] void always_instrument() { + printf("always instrumented called\n"); +} + +int main() { + __xray_set_handler(test_handler); + always_instrument(); + // CHECK: always instrumented called + auto status = __xray_patch(); + printf("patching status: %d\n", static_cast(status)); + // CHECK-NEXT: patching status: 1 + always_instrument(); + // CHECK-NEXT: called: {{.*}}, type=0 + // CHECK-NEXT: always instrumented called + // CHECK-NEXT: called: {{.*}}, type=1 + status = __xray_unpatch(); + printf("patching status: %d\n", static_cast(status)); + // CHECK-NEXT: patching status: 1 + always_instrument(); + // CHECK-NEXT: always instrumented called + status = __xray_patch(); + printf("patching status: %d\n", static_cast(status)); + // CHECK-NEXT: patching status: 1 + __xray_remove_handler(); + always_instrument(); + // CHECK-NEXT: always instrumented called + status = __xray_unpatch(); + printf("patching status: %d\n", static_cast(status)); + // CHECK-NEXT: patching status: 1 +} Index: test/xray/lit.cfg =================================================================== --- /dev/null +++ test/xray/lit.cfg @@ -0,0 +1,34 @@ +# -*- Python -*- + +import os + +# Setup config name. +config.name = 'XRay' + config.name_suffix + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +# Setup default compiler flags use with -fxray-instrument option. +clang_xray_cflags = (['-fxray-instrument', config.target_cflags]) +clang_xray_cxxflags = config.cxx_mode_flags + clang_xray_cflags + + +def build_invocation(compile_flags): + return ' ' + ' '.join([config.clang] + compile_flags) + ' ' + +# Setup substitutions. +config.substitutions.append( + ('%clang ', build_invocation([config.target_cflags]))) +config.substitutions.append( + ('%clangxx ', + build_invocation(config.cxx_mode_flags + [config.target_cflags]))) +config.substitutions.append( + ('%clang_xray ', build_invocation(clang_xray_cflags))) +config.substitutions.append( + ('%clangxx_xray', build_invocation(clang_xray_cxxflags))) + +# Default test suffixes. +config.suffixes = ['.c', '.cc', '.cpp'] + +if config.host_os not in ['Linux'] or config.host_arch.find('64') == -1: + config.unsupported = True Index: test/xray/lit.site.cfg.in =================================================================== --- /dev/null +++ test/xray/lit.site.cfg.in @@ -0,0 +1,13 @@ +@LIT_SITE_CFG_IN_HEADER@ + +# Tool-specific config options. +config.name_suffix = "@XRAY_TEST_CONFIG_SUFFIX@" +config.xray_lit_source_dir = "@XRAY_LIT_SOURCE_DIR@" +config.target_cflags = "@XRAY_TEST_TARGET_CFLAGS@" +config.target_arch = "@XRAY_TEST_TARGET_ARCH@" + +# Load common config for all compiler-rt lit tests +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@XRAY_LIT_SOURCE_DIR@/lit.cfg")