Skip to content
This repository was archived by the owner on Apr 23, 2020. It is now read-only.

Commit f9ca15b

Browse files
committed
[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 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@288775 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent d1dc04e commit f9ca15b

9 files changed

+368
-17
lines changed

lib/xray/CMakeLists.txt

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
# Build for the XRay runtime support library.
22

3+
# Core XRay runtime library implementation files.
34
set(XRAY_SOURCES
45
xray_init.cc
56
xray_interface.cc
67
xray_flags.cc
7-
xray_inmemory_log.cc
8-
)
8+
xray_inmemory_log.cc)
9+
10+
# XRay flight data recorder (FDR) implementation files.
11+
set(XRAY_FDR_SOURCES
12+
xray_buffer_queue.cc)
913

1014
set(x86_64_SOURCES
1115
xray_x86_64.cc
@@ -28,31 +32,48 @@ include_directories(..)
2832
include_directories(../../include)
2933

3034
set(XRAY_CFLAGS ${SANITIZER_COMMON_CFLAGS})
31-
3235
set(XRAY_COMMON_DEFINITIONS XRAY_HAS_EXCEPTIONS=1)
3336
append_list_if(
3437
COMPILER_RT_HAS_XRAY_COMPILER_FLAG XRAY_SUPPORTED=1 XRAY_COMMON_DEFINITIONS)
3538

3639
add_compiler_rt_object_libraries(RTXray
37-
ARCHS ${XRAY_SUPPORTED_ARCH}
38-
SOURCES ${XRAY_SOURCES} CFLAGS ${XRAY_CFLAGS}
39-
DEFS ${XRAY_COMMON_DEFINITIONS})
40+
ARCHS ${XRAY_SUPPORTED_ARCH}
41+
SOURCES ${XRAY_SOURCES} CFLAGS ${XRAY_CFLAGS}
42+
DEFS ${XRAY_COMMON_DEFINITIONS})
43+
44+
add_compiler_rt_object_libraries(RTXrayFDR
45+
ARCHS ${XRAY_SUPPORTED_ARCH}
46+
SOURCES ${XRAY_FDR_SOURCES} CFLAGS ${XRAY_CFLAGS}
47+
DEFS ${XRAY_COMMON_DEFINITIONS})
4048

4149
add_compiler_rt_component(xray)
50+
add_compiler_rt_component(xray-fdr)
4251

4352
set(XRAY_COMMON_RUNTIME_OBJECT_LIBS
4453
RTSanitizerCommon
4554
RTSanitizerCommonLibc)
4655

47-
foreach (arch ${XRAY_SUPPORTED_ARCH})
48-
if (CAN_TARGET_${arch})
49-
add_compiler_rt_runtime(clang_rt.xray
50-
STATIC
51-
ARCHS ${arch}
52-
SOURCES ${${arch}_SOURCES}
53-
CFLAGS ${XRAY_CFLAGS}
54-
DEFS ${XRAY_COMMON_DEFINITIONS}
55-
OBJECT_LIBS ${XRAY_COMMON_RUNTIME_OBJECT_LIBS}
56-
PARENT_TARGET xray)
57-
endif ()
56+
foreach(arch ${XRAY_SUPPORTED_ARCH})
57+
if(CAN_TARGET_${arch})
58+
add_compiler_rt_runtime(clang_rt.xray
59+
STATIC
60+
ARCHS ${arch}
61+
SOURCES ${${arch}_SOURCES}
62+
CFLAGS ${XRAY_CFLAGS}
63+
DEFS ${XRAY_COMMON_DEFINITIONS}
64+
OBJECT_LIBS ${XRAY_COMMON_RUNTIME_OBJECT_LIBS}
65+
PARENT_TARGET xray)
66+
add_compiler_rt_runtime(clang_rt.xray-fdr
67+
STATIC
68+
ARCHS ${arch}
69+
SOURCES ${XRAY_FDR_SOURCES}
70+
CFLAGS ${XRAY_CFLAGS}
71+
DEFS ${XRAY_COMMON_DEFINITIONS}
72+
OBJECT_LIBS ${XRAY_COMMON_RUNTIME_OBJECT_LIBS}
73+
PARENT_TARGET xray-fdr)
74+
endif()
5875
endforeach()
76+
77+
if(COMPILER_RT_INCLUDE_TESTS)
78+
add_subdirectory(tests)
79+
endif()

lib/xray/tests/CMakeLists.txt

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
include_directories(..)
2+
3+
add_custom_target(XRayUnitTests)
4+
set_target_properties(XRayUnitTests PROPERTIES FOLDER "XRay unittests")
5+
6+
set(XRAY_UNITTEST_CFLAGS
7+
${XRAY_CFLAGS}
8+
${COMPILER_RT_UNITTEST_CFLAGS}
9+
${COMPILER_RT_GTEST_CFLAGS}
10+
-I${COMPILER_RT_SOURCE_DIR}/include
11+
-I${COMPILER_RT_SOURCE_DIR}/lib/xray)
12+
13+
macro(xray_compile obj_list source arch)
14+
get_filename_component(basename ${source} NAME)
15+
set(output_obj "${basename}.${arch}.o")
16+
get_target_flags_for_arch(${arch} TARGET_CFLAGS)
17+
if(NOT COMPILER_RT_STANDALONE_BUILD)
18+
list(APPEND COMPILE_DEPS gtest_main xray-fdr)
19+
endif()
20+
clang_compile(${output_obj} ${source}
21+
CFLAGS ${XRAY_UNITTEST_CFLAGS} ${TARGET_CFLAGS}
22+
DEPS ${COMPILE_DEPS})
23+
list(APPEND ${obj_list} ${output_obj})
24+
endmacro()
25+
26+
macro(add_xray_unittest testname)
27+
set(XRAY_TEST_ARCH ${XRAY_SUPPORTED_ARCH})
28+
if (APPLE)
29+
darwin_filter_host_archs(XRAY_SUPPORTED_ARCH)
30+
endif()
31+
if(UNIX)
32+
foreach(arch ${XRAY_TEST_ARCH})
33+
cmake_parse_arguments(TEST "" "" "SOURCES;HEADERS" ${ARGN})
34+
set(TEST_OBJECTS)
35+
foreach(SOURCE ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE})
36+
xray_compile(TEST_OBJECTS ${SOURCE} ${arch} ${TEST_HEADERS})
37+
endforeach()
38+
get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS)
39+
set(TEST_DEPS ${TEST_OBJECTS})
40+
if(NOT COMPILER_RT_STANDALONE_BUILD)
41+
list(APPEND TEST_DEPS gtest_main xray-fdr)
42+
endif()
43+
if(NOT APPLE)
44+
add_compiler_rt_test(XRayUnitTests ${testname}
45+
OBJECTS ${TEST_OBJECTS}
46+
DEPS ${TEST_DEPS}
47+
LINK_FLAGS ${TARGET_LINK_FLAGS}
48+
-lstdc++ -lm ${CMAKE_THREAD_LIBS_INIT}
49+
-L${COMPILER_RT_LIBRARY_OUTPUT_DIR} -lclang_rt.xray-fdr-${arch})
50+
endif()
51+
# FIXME: Figure out how to run even just the unit tests on APPLE.
52+
endforeach()
53+
endif()
54+
endmacro()
55+
56+
if(COMPILER_RT_CAN_EXECUTE_TESTS AND NOT ANDROID)
57+
add_subdirectory(unit)
58+
endif()

lib/xray/tests/unit/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
add_xray_unittest(XRayBufferQueueTest SOURCES
2+
buffer_queue_test.cc xray_unit_test_main.cc)
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//===-- buffer_queue_test.cc ----------------------------------------------===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
//
10+
// This file is a part of XRay, a function call tracing system.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
#include "xray_buffer_queue.h"
14+
#include "gtest/gtest.h"
15+
16+
#include <future>
17+
#include <unistd.h>
18+
19+
namespace __xray {
20+
21+
static constexpr size_t kSize = 4096;
22+
23+
TEST(BufferQueueTest, API) { BufferQueue Buffers(kSize, 1); }
24+
25+
TEST(BufferQueueTest, GetAndRelease) {
26+
BufferQueue Buffers(kSize, 1);
27+
BufferQueue::Buffer Buf;
28+
ASSERT_FALSE(Buffers.getBuffer(Buf));
29+
ASSERT_NE(nullptr, Buf.Buffer);
30+
ASSERT_FALSE(Buffers.releaseBuffer(Buf));
31+
ASSERT_EQ(nullptr, Buf.Buffer);
32+
}
33+
34+
TEST(BufferQueueTest, GetUntilFailed) {
35+
BufferQueue Buffers(kSize, 1);
36+
BufferQueue::Buffer Buf0;
37+
EXPECT_FALSE(Buffers.getBuffer(Buf0));
38+
BufferQueue::Buffer Buf1;
39+
EXPECT_EQ(std::errc::not_enough_memory, Buffers.getBuffer(Buf1));
40+
EXPECT_FALSE(Buffers.releaseBuffer(Buf0));
41+
}
42+
43+
TEST(BufferQueueTest, ReleaseUnknown) {
44+
BufferQueue Buffers(kSize, 1);
45+
BufferQueue::Buffer Buf;
46+
Buf.Buffer = reinterpret_cast<void *>(0xdeadbeef);
47+
Buf.Size = kSize;
48+
EXPECT_EQ(std::errc::argument_out_of_domain, Buffers.releaseBuffer(Buf));
49+
}
50+
51+
TEST(BufferQueueTest, ErrorsWhenFinalising) {
52+
BufferQueue Buffers(kSize, 2);
53+
BufferQueue::Buffer Buf;
54+
ASSERT_FALSE(Buffers.getBuffer(Buf));
55+
ASSERT_NE(nullptr, Buf.Buffer);
56+
ASSERT_FALSE(Buffers.finalize());
57+
BufferQueue::Buffer OtherBuf;
58+
ASSERT_EQ(std::errc::state_not_recoverable, Buffers.getBuffer(OtherBuf));
59+
ASSERT_EQ(std::errc::state_not_recoverable, Buffers.finalize());
60+
ASSERT_FALSE(Buffers.releaseBuffer(Buf));
61+
}
62+
63+
TEST(BufferQueueTest, MultiThreaded) {
64+
BufferQueue Buffers(kSize, 100);
65+
auto F = [&] {
66+
BufferQueue::Buffer B;
67+
while (!Buffers.getBuffer(B)) {
68+
Buffers.releaseBuffer(B);
69+
}
70+
};
71+
auto T0 = std::async(std::launch::async, F);
72+
auto T1 = std::async(std::launch::async, F);
73+
auto T2 = std::async(std::launch::async, [&] {
74+
while (!Buffers.finalize())
75+
;
76+
});
77+
F();
78+
}
79+
80+
} // namespace __xray
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//===-- xray_unit_test_main.cc --------------------------------------------===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
//
10+
// This file is a part of XRay, a function call tracing system.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
#include "gtest/gtest.h"
14+
15+
int main(int argc, char **argv) {
16+
testing::InitGoogleTest(&argc, argv);
17+
return RUN_ALL_TESTS();
18+
}

lib/xray/xray_buffer_queue.cc

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//===-- xray_buffer_queue.cc -----------------------------------*- C++ -*-===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
//
10+
// This file is a part of XRay, a dynamic runtime instruementation system.
11+
//
12+
// Defines the interface for a buffer queue implementation.
13+
//
14+
//===----------------------------------------------------------------------===//
15+
#include "xray_buffer_queue.h"
16+
#include <cassert>
17+
#include <cstdlib>
18+
19+
using namespace __xray;
20+
21+
BufferQueue::BufferQueue(std::size_t B, std::size_t N)
22+
: BufferSize(B), Buffers(N) {
23+
for (auto &Buf : Buffers) {
24+
void *Tmp = malloc(BufferSize);
25+
Buf.Buffer = Tmp;
26+
Buf.Size = B;
27+
if (Tmp != 0)
28+
OwnedBuffers.insert(Tmp);
29+
}
30+
}
31+
32+
std::error_code BufferQueue::getBuffer(Buffer &Buf) {
33+
if (Finalizing.load(std::memory_order_acquire))
34+
return std::make_error_code(std::errc::state_not_recoverable);
35+
std::lock_guard<std::mutex> Guard(Mutex);
36+
if (Buffers.empty())
37+
return std::make_error_code(std::errc::not_enough_memory);
38+
Buf = Buffers.front();
39+
Buffers.pop_front();
40+
return {};
41+
}
42+
43+
std::error_code BufferQueue::releaseBuffer(Buffer &Buf) {
44+
if (OwnedBuffers.count(Buf.Buffer) == 0)
45+
return std::make_error_code(std::errc::argument_out_of_domain);
46+
std::lock_guard<std::mutex> Guard(Mutex);
47+
Buffers.push_back(Buf);
48+
Buf.Buffer = nullptr;
49+
Buf.Size = BufferSize;
50+
return {};
51+
}
52+
53+
std::error_code BufferQueue::finalize() {
54+
if (Finalizing.exchange(true, std::memory_order_acq_rel))
55+
return std::make_error_code(std::errc::state_not_recoverable);
56+
return {};
57+
}
58+
59+
BufferQueue::~BufferQueue() {
60+
for (auto &Buf : Buffers) {
61+
free(Buf.Buffer);
62+
Buf.Buffer = nullptr;
63+
Buf.Size = 0;
64+
}
65+
}

lib/xray/xray_buffer_queue.h

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//===-- xray_buffer_queue.h ------------------------------------*- C++ -*-===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
//
10+
// This file is a part of XRay, a dynamic runtime instrumentation system.
11+
//
12+
// Defines the interface for a buffer queue implementation.
13+
//
14+
//===----------------------------------------------------------------------===//
15+
#ifndef XRAY_BUFFER_QUEUE_H
16+
#define XRAY_BUFFER_QUEUE_H
17+
18+
#include <atomic>
19+
#include <cstdint>
20+
#include <deque>
21+
#include <mutex>
22+
#include <system_error>
23+
#include <unordered_set>
24+
25+
namespace __xray {
26+
27+
/// BufferQueue implements a circular queue of fixed sized buffers (much like a
28+
/// freelist) but is concerned mostly with making it really quick to initialise,
29+
/// finalise, and get/return buffers to the queue. This is one key component of
30+
/// the "flight data recorder" (FDR) mode to support ongoing XRay function call
31+
/// trace collection.
32+
class BufferQueue {
33+
public:
34+
struct Buffer {
35+
void *Buffer = nullptr;
36+
std::size_t Size = 0;
37+
};
38+
39+
private:
40+
std::size_t BufferSize;
41+
std::deque<Buffer> Buffers;
42+
std::mutex Mutex;
43+
std::unordered_set<void *> OwnedBuffers;
44+
std::atomic<bool> Finalizing;
45+
46+
public:
47+
/// Initialise a queue of size |N| with buffers of size |B|.
48+
BufferQueue(std::size_t B, std::size_t N);
49+
50+
/// Updates |Buf| to contain the pointer to an appropriate buffer. Returns an
51+
/// error in case there are no available buffers to return when we will run
52+
/// over the upper bound for the total buffers.
53+
///
54+
/// Requirements:
55+
/// - BufferQueue is not finalising.
56+
///
57+
/// Returns:
58+
/// - std::errc::not_enough_memory on exceeding MaxSize.
59+
/// - no error when we find a Buffer.
60+
/// - std::errc::state_not_recoverable on finalising BufferQueue.
61+
std::error_code getBuffer(Buffer &Buf);
62+
63+
/// Updates |Buf| to point to nullptr, with size 0.
64+
///
65+
/// Returns:
66+
/// - ...
67+
std::error_code releaseBuffer(Buffer &Buf);
68+
69+
bool finalizing() const { return Finalizing.load(std::memory_order_acquire); }
70+
71+
// Sets the state of the BufferQueue to finalizing, which ensures that:
72+
//
73+
// - All subsequent attempts to retrieve a Buffer will fail.
74+
// - All releaseBuffer operations will not fail.
75+
//
76+
// After a call to finalize succeeds, all subsequent calls to finalize will
77+
// fail with std::errc::state_not_recoverable.
78+
std::error_code finalize();
79+
80+
// Cleans up allocated buffers.
81+
~BufferQueue();
82+
};
83+
84+
} // namespace __xray
85+
86+
#endif // XRAY_BUFFER_QUEUE_H

0 commit comments

Comments
 (0)