Index: lib/xray/CMakeLists.txt =================================================================== --- lib/xray/CMakeLists.txt +++ lib/xray/CMakeLists.txt @@ -1,11 +1,15 @@ # Build for the XRay runtime support library. +# Core XRay runtime library implementation files. set(XRAY_SOURCES xray_init.cc xray_interface.cc xray_flags.cc - xray_inmemory_log.cc -) + xray_inmemory_log.cc) + +# XRay flight data recorder (FDR) implementation files. +set(XRAY_FDR_SOURCES + xray_buffer_queue.cc) set(x86_64_SOURCES xray_x86_64.cc @@ -23,31 +27,48 @@ include_directories(../../include) set(XRAY_CFLAGS ${SANITIZER_COMMON_CFLAGS}) - set(XRAY_COMMON_DEFINITIONS XRAY_HAS_EXCEPTIONS=1) append_list_if( COMPILER_RT_HAS_XRAY_COMPILER_FLAG XRAY_SUPPORTED=1 XRAY_COMMON_DEFINITIONS) add_compiler_rt_object_libraries(RTXray - ARCHS ${XRAY_SUPPORTED_ARCH} - SOURCES ${XRAY_SOURCES} CFLAGS ${XRAY_CFLAGS} - DEFS ${XRAY_COMMON_DEFINITIONS}) + ARCHS ${XRAY_SUPPORTED_ARCH} + SOURCES ${XRAY_SOURCES} CFLAGS ${XRAY_CFLAGS} + DEFS ${XRAY_COMMON_DEFINITIONS}) + +add_compiler_rt_object_libraries(RTXrayFDR + ARCHS ${XRAY_SUPPORTED_ARCH} + SOURCES ${XRAY_FDR_SOURCES} CFLAGS ${XRAY_CFLAGS} + DEFS ${XRAY_COMMON_DEFINITIONS}) add_compiler_rt_component(xray) +add_compiler_rt_component(xray-fdr) set(XRAY_COMMON_RUNTIME_OBJECT_LIBS RTSanitizerCommon RTSanitizerCommonLibc) -foreach (arch ${XRAY_SUPPORTED_ARCH}) - if (CAN_TARGET_${arch}) - add_compiler_rt_runtime(clang_rt.xray - STATIC - ARCHS ${arch} - SOURCES ${${arch}_SOURCES} - CFLAGS ${XRAY_CFLAGS} - DEFS ${XRAY_COMMON_DEFINITIONS} - OBJECT_LIBS ${XRAY_COMMON_RUNTIME_OBJECT_LIBS} - PARENT_TARGET xray) - endif () +foreach(arch ${XRAY_SUPPORTED_ARCH}) + if(CAN_TARGET_${arch}) + add_compiler_rt_runtime(clang_rt.xray + STATIC + ARCHS ${arch} + SOURCES ${${arch}_SOURCES} + CFLAGS ${XRAY_CFLAGS} + DEFS ${XRAY_COMMON_DEFINITIONS} + OBJECT_LIBS ${XRAY_COMMON_RUNTIME_OBJECT_LIBS} + PARENT_TARGET xray) + add_compiler_rt_runtime(clang_rt.xray-fdr + STATIC + ARCHS ${arch} + SOURCES ${XRAY_FDR_SOURCES} + CFLAGS ${XRAY_CFLAGS} + DEFS ${XRAY_COMMON_DEFINITIONS} + OBJECT_LIBS ${XRAY_COMMON_RUNTIME_OBJECT_LIBS} + PARENT_TARGET xray-fdr) + endif() endforeach() + +if(COMPILER_RT_INCLUDE_TESTS) + add_subdirectory(tests) +endif() Index: lib/xray/tests/CMakeLists.txt =================================================================== --- /dev/null +++ lib/xray/tests/CMakeLists.txt @@ -0,0 +1,58 @@ +include_directories(..) + +add_custom_target(XRayUnitTests) +set_target_properties(XRayUnitTests PROPERTIES FOLDER "XRay unittests") + +set(XRAY_UNITTEST_CFLAGS + ${XRAY_CFLAGS} + ${COMPILER_RT_UNITTEST_CFLAGS} + ${COMPILER_RT_GTEST_CFLAGS} + -I${COMPILER_RT_SOURCE_DIR}/include + -I${COMPILER_RT_SOURCE_DIR}/lib/xray) + +macro(xray_compile obj_list source arch) + get_filename_component(basename ${source} NAME) + set(output_obj "${basename}.${arch}.o") + get_target_flags_for_arch(${arch} TARGET_CFLAGS) + if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND COMPILE_DEPS gtest_main xray-fdr) + endif() + clang_compile(${output_obj} ${source} + CFLAGS ${XRAY_UNITTEST_CFLAGS} ${TARGET_CFLAGS} + DEPS ${COMPILE_DEPS}) + list(APPEND ${obj_list} ${output_obj}) +endmacro() + +macro(add_xray_unittest testname) + set(XRAY_TEST_ARCH ${XRAY_SUPPORTED_ARCH}) + if (APPLE) + darwin_filter_host_archs(XRAY_SUPPORTED_ARCH) + endif() + if(UNIX) + foreach(arch ${XRAY_TEST_ARCH}) + cmake_parse_arguments(TEST "" "" "SOURCES;HEADERS" ${ARGN}) + set(TEST_OBJECTS) + foreach(SOURCE ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE}) + xray_compile(TEST_OBJECTS ${SOURCE} ${arch} ${TEST_HEADERS}) + endforeach() + get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS) + set(TEST_DEPS ${TEST_OBJECTS}) + if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND TEST_DEPS gtest_main xray-fdr) + endif() + if(NOT APPLE) + add_compiler_rt_test(XRayUnitTests ${testname} + OBJECTS ${TEST_OBJECTS} + DEPS ${TEST_DEPS} + LINK_FLAGS ${TARGET_LINK_FLAGS} + -lstdc++ -lm ${CMAKE_THREAD_LIBS_INIT} + -L${COMPILER_RT_LIBRARY_OUTPUT_DIR} -lclang_rt.xray-fdr-${arch}) + endif() + # FIXME: Figure out how to run even just the unit tests on APPLE. + endforeach() + endif() +endmacro() + +if(COMPILER_RT_CAN_EXECUTE_TESTS AND NOT ANDROID) + add_subdirectory(unit) +endif() Index: lib/xray/tests/unit/CMakeLists.txt =================================================================== --- /dev/null +++ lib/xray/tests/unit/CMakeLists.txt @@ -0,0 +1,2 @@ +add_xray_unittest(XRayBufferQueueTest SOURCES + buffer_queue_test.cc xray_unit_test_main.cc) Index: lib/xray/tests/unit/buffer_queue_test.cc =================================================================== --- /dev/null +++ lib/xray/tests/unit/buffer_queue_test.cc @@ -0,0 +1,77 @@ +//===-- buffer_queue_test.cc ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of XRay, a function call tracing system. +// +//===----------------------------------------------------------------------===// +#include "xray_buffer_queue.h" +#include "gtest/gtest.h" + +#include +#include + +namespace __xray { + +static constexpr size_t kSize = 4096; + +TEST(BufferQueueTest, API) { BufferQueue Buffers(kSize, 1); } + +TEST(BufferQueueTest, GetAndRelease) { + BufferQueue Buffers(kSize, 1); + BufferQueue::Buffer Buf; + ASSERT_FALSE(Buffers.getBuffer(Buf)); + ASSERT_NE(nullptr, Buf.Buffer); + ASSERT_FALSE(Buffers.releaseBuffer(Buf)); + ASSERT_EQ(nullptr, Buf.Buffer); +} + +TEST(BufferQueueTest, GetUntilFailed) { + BufferQueue Buffers(kSize, 1); + BufferQueue::Buffer Buf0; + EXPECT_FALSE(Buffers.getBuffer(Buf0)); + BufferQueue::Buffer Buf1; + EXPECT_EQ(std::errc::not_enough_memory, Buffers.getBuffer(Buf1)); + EXPECT_FALSE(Buffers.releaseBuffer(Buf0)); +} + +TEST(BufferQueueTest, ReleaseUnknown) { + BufferQueue Buffers(kSize, 1); + BufferQueue::Buffer Buf; + Buf.Buffer = reinterpret_cast(0xdeadbeef); + Buf.Size = kSize; + EXPECT_EQ(std::errc::argument_out_of_domain, Buffers.releaseBuffer(Buf)); +} + +TEST(BufferQueueTest, ErrorsWhenFinalising) { + BufferQueue Buffers(kSize, 2); + BufferQueue::Buffer Buf; + ASSERT_FALSE(Buffers.getBuffer(Buf)); + ASSERT_NE(nullptr, Buf.Buffer); + ASSERT_FALSE(Buffers.finalize()); + BufferQueue::Buffer OtherBuf; + ASSERT_EQ(std::errc::state_not_recoverable, Buffers.getBuffer(OtherBuf)); + ASSERT_EQ(std::errc::state_not_recoverable, Buffers.finalize()); + ASSERT_FALSE(Buffers.releaseBuffer(Buf)); +} + +TEST(BufferQueueTest, MultiThreaded) { + BufferQueue Buffers(kSize, 100); + auto F = [&] { + BufferQueue::Buffer B; + while (!Buffers.getBuffer(B)) { + Buffers.releaseBuffer(B); + } + }; + auto T0 = std::async(F); + auto T1 = std::async(F); + auto T2 = std::async([&] { Buffers.finalize(); }); + F(); +} + +} // namespace __xray Index: lib/xray/tests/unit/xray_unit_test_main.cc =================================================================== --- /dev/null +++ lib/xray/tests/unit/xray_unit_test_main.cc @@ -0,0 +1,18 @@ +//===-- xray_unit_test_main.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of XRay, a function call tracing system. +// +//===----------------------------------------------------------------------===// +#include "gtest/gtest.h" + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} Index: lib/xray/xray_buffer_queue.h =================================================================== --- /dev/null +++ lib/xray/xray_buffer_queue.h @@ -0,0 +1,86 @@ +//===-- xray_buffer_queue.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of XRay, a dynamic runtime instrumentation system. +// +// Defines the interface for a buffer queue implementation. +// +//===----------------------------------------------------------------------===// +#ifndef XRAY_BUFFER_QUEUE_H +#define XRAY_BUFFER_QUEUE_H + +#include +#include +#include +#include +#include +#include + +namespace __xray { + +/// BufferQueue implements a circular queue of fixed sized buffers (much like a +/// freelist) but is concerned mostly with making it really quick to initialise, +/// finalise, and get/return buffers to the queue. This is one key component of +/// the "flight data recorder" (FDR) mode to support ongoing XRay function call +/// trace collection. +class BufferQueue { +public: + struct Buffer { + void *Buffer = nullptr; + std::size_t Size = 0; + }; + +private: + std::size_t BufferSize; + std::deque Buffers; + std::mutex Mutex; + std::unordered_set OwnedBuffers; + std::atomic Finalizing; + +public: + /// Initialise a queue of size |N| with buffers of size |B|. + BufferQueue(std::size_t B, std::size_t N); + + /// Updates |Buf| to contain the pointer to an appropriate buffer. Returns an + /// error in case there are no available buffers to return when we will run + /// over the upper bound for the total buffers. + /// + /// Requirements: + /// - BufferQueue is not finalising. + /// + /// Returns: + /// - std::errc::not_enough_memory on exceeding MaxSize. + /// - no error when we find a Buffer. + /// - std::errc::state_not_recoverable on finalising BufferQueue. + std::error_code getBuffer(Buffer &Buf); + + /// Updates |Buf| to point to nullptr, with size 0. + /// + /// Returns: + /// - ... + std::error_code releaseBuffer(Buffer &Buf); + + bool finalizing() const { return Finalizing.load(std::memory_order_acquire); } + + // Sets the state of the BufferQueue to finalizing, which ensures that: + // + // - All subsequent attempts to retrieve a Buffer will fail. + // - All releaseBuffer operations will not fail. + // + // After a call to finalize succeeds, all subsequent calls to finalize will + // fail with std::errc::state_not_recoverable. + std::error_code finalize(); + + // Cleans up allocated buffers. + ~BufferQueue(); +}; + +} // namespace __xray + +#endif // XRAY_BUFFER_QUEUE_H Index: lib/xray/xray_buffer_queue.cc =================================================================== --- /dev/null +++ lib/xray/xray_buffer_queue.cc @@ -0,0 +1,65 @@ +//===-- xray_buffer_queue.cc -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of XRay, a dynamic runtime instruementation system. +// +// Defines the interface for a buffer queue implementation. +// +//===----------------------------------------------------------------------===// +#include "xray_buffer_queue.h" +#include +#include + +using namespace __xray; + +BufferQueue::BufferQueue(std::size_t B, std::size_t N) + : BufferSize(B), Buffers(N) { + for (auto &Buf : Buffers) { + void *Tmp = malloc(BufferSize); + Buf.Buffer = Tmp; + Buf.Size = B; + if (Tmp != 0) + OwnedBuffers.insert(Tmp); + } +} + +std::error_code BufferQueue::getBuffer(Buffer &Buf) { + if (Finalizing.load(std::memory_order_acquire)) + return std::make_error_code(std::errc::state_not_recoverable); + std::lock_guard Guard(Mutex); + if (Buffers.empty()) + return std::make_error_code(std::errc::not_enough_memory); + Buf = Buffers.front(); + Buffers.pop_front(); + return {}; +} + +std::error_code BufferQueue::releaseBuffer(Buffer &Buf) { + if (OwnedBuffers.count(Buf.Buffer) == 0) + return std::make_error_code(std::errc::argument_out_of_domain); + std::lock_guard Guard(Mutex); + Buffers.push_back(Buf); + Buf.Buffer = nullptr; + Buf.Size = BufferSize; + return {}; +} + +std::error_code BufferQueue::finalize() { + if (Finalizing.exchange(true, std::memory_order_acq_rel)) + return std::make_error_code(std::errc::state_not_recoverable); + return {}; +} + +BufferQueue::~BufferQueue() { + for (auto &Buf : Buffers) { + free(Buf.Buffer); + Buf.Buffer = nullptr; + Buf.Size = 0; + } +} Index: test/xray/CMakeLists.txt =================================================================== --- test/xray/CMakeLists.txt +++ test/xray/CMakeLists.txt @@ -35,6 +35,15 @@ endforeach() endif() +# Add unit tests. +if(COMPILER_RT_INCLUDE_TESTS) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg) + list(APPEND XRAY_TEST_DEPS XRayUnitTests) + list(APPEND XRAY_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/Unit) +endif() + add_lit_testsuite(check-xray "Running the XRay tests" ${XRAY_TESTSUITES} DEPENDS ${XRAY_TEST_DEPS}) Index: test/xray/Unit/lit.site.cfg.in =================================================================== --- /dev/null +++ test/xray/Unit/lit.site.cfg.in @@ -0,0 +1,12 @@ +@LIT_SITE_CFG_IN_HEADER@ + +import os + +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/unittests/lit.common.unit.configured") + +# Setup config name. +config.name = 'XRay-Unit' + +config.test_exec_root = "@COMPILER_RT_BINARY_DIR@/lib/xray/tests" +config.test_source_root = config.test_exec_root