forked from OSchip/llvm-project
[XRay][compiler-rt] XRay Buffer Queue
This implements a simple buffer queue to manage a pre-allocated queue of fixed-sized buffers to hold XRay records. We need this to support Flight Data Recorder (FDR) mode. We also implement this as a sub-library first to allow for development before actually using it in an implementation. Some important properties of the buffer queue: - Thread-safe enqueueing/dequeueing of fixed-size buffers. - Pre-allocation of buffers at construction. This is a re-roll of the previous attempt to submit, because it caused failures in arm and aarch64. Reviewers: majnemer, echristo, rSerge Subscribers: tberghammer, danalbert, srhines, modocache, mehdi_amini, mgorny, llvm-commits Differential Revision: https://reviews.llvm.org/D26232 llvm-svn: 288775
This commit is contained in:
parent
8b058aec1d
commit
abe04e3295
|
@ -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
|
||||
|
@ -28,31 +32,48 @@ include_directories(..)
|
|||
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()
|
||||
|
|
|
@ -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()
|
|
@ -0,0 +1,2 @@
|
|||
add_xray_unittest(XRayBufferQueueTest SOURCES
|
||||
buffer_queue_test.cc xray_unit_test_main.cc)
|
|
@ -0,0 +1,80 @@
|
|||
//===-- 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 <future>
|
||||
#include <unistd.h>
|
||||
|
||||
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<void *>(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(std::launch::async, F);
|
||||
auto T1 = std::async(std::launch::async, F);
|
||||
auto T2 = std::async(std::launch::async, [&] {
|
||||
while (!Buffers.finalize())
|
||||
;
|
||||
});
|
||||
F();
|
||||
}
|
||||
|
||||
} // namespace __xray
|
|
@ -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();
|
||||
}
|
|
@ -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 <cassert>
|
||||
#include <cstdlib>
|
||||
|
||||
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<std::mutex> 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<std::mutex> 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;
|
||||
}
|
||||
}
|
|
@ -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 <atomic>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <system_error>
|
||||
#include <unordered_set>
|
||||
|
||||
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<Buffer> Buffers;
|
||||
std::mutex Mutex;
|
||||
std::unordered_set<void *> OwnedBuffers;
|
||||
std::atomic<bool> 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
|
|
@ -25,6 +25,15 @@ if (COMPILER_RT_BUILD_XRAY AND COMPILER_RT_HAS_XRAY)
|
|||
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})
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue