Skip to content

Commit 635ae70

Browse files
committed
[XRay][profiler] Part 1: XRay Allocator and Array Implementations
Summary: This change is part of the larger XRay Profiling Mode effort. Here we implement an arena allocator, for fixed sized buffers used in a segmented array implementation. This change adds the segmented array data structure, which relies on the allocator to provide and maintain the storage for the segmented array. Key features of the `Allocator` type: * It uses cache-aligned blocks, intended to host the actual data. These blocks are cache-line-size multiples of contiguous bytes. * The `Allocator` has a maximum memory budget, set at construction time. This allows us to cap the amount of data each specific `Allocator` instance is responsible for. * Upon destruction, the `Allocator` will clean up the storage it's used, handing it back to the internal allocator used in sanitizer_common. Key features of the `Array` type: * Each segmented array is always backed by an `Allocator`, which is either user-provided or uses a global allocator. * When an `Array` grows, it grows by appending a segment that's fixed-sized. The size of each segment is computed by the number of elements of type `T` that can fit into cache line multiples. * An `Array` does not return memory to the `Allocator`, but it can keep track of the current number of "live" objects it stores. * When an `Array` is destroyed, it will not return memory to the `Allocator`. Users should clean up the `Allocator` independently of the `Array`. * The `Array` type keeps a freelist of the chunks it's used before, so that trimming and growing will re-use previously allocated chunks. These basic data structures are used by the XRay Profiling Mode implementation to implement efficient and cache-aware storage for data that's typically read-and-write heavy for tracking latency information. We're relying on the cache line characteristics of the architecture to provide us good data isolation and cache friendliness, when we're performing operations like searching for elements and/or updating data hosted in these cache lines. Reviewers: echristo, pelikan, kpw Subscribers: mgorny, llvm-commits Differential Revision: https://reviews.llvm.org/D45756 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@331141 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent e273986 commit 635ae70

File tree

6 files changed

+711
-16
lines changed

6 files changed

+711
-16
lines changed

lib/xray/CMakeLists.txt

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,57 @@
22

33
# XRay runtime library implementation files.
44
set(XRAY_SOURCES
5-
xray_init.cc
6-
xray_flags.cc
7-
xray_interface.cc
8-
xray_log_interface.cc
9-
xray_utils.cc)
5+
xray_init.cc
6+
xray_flags.cc
7+
xray_interface.cc
8+
xray_log_interface.cc
9+
xray_utils.cc)
1010

1111
# Implementation files for all XRay modes.
1212
set(XRAY_FDR_MODE_SOURCES
13-
xray_buffer_queue.cc
14-
xray_fdr_logging.cc)
13+
xray_buffer_queue.cc
14+
xray_fdr_logging.cc)
1515

1616
set(XRAY_BASIC_MODE_SOURCES
17-
xray_inmemory_log.cc)
17+
xray_inmemory_log.cc)
18+
1819

1920
# Implementation files for all XRay architectures.
20-
set(aarch64_SOURCES xray_AArch64.cc xray_trampoline_AArch64.S)
21-
set(arm_SOURCES xray_arm.cc xray_trampoline_arm.S)
22-
set(armhf_SOURCES ${arm_SOURCES})
23-
set(mips_SOURCES xray_mips.cc xray_trampoline_mips.S)
24-
set(mipsel_SOURCES xray_mips.cc xray_trampoline_mips.S)
25-
set(mips64_SOURCES xray_mips64.cc xray_trampoline_mips64.S)
26-
set(mips64el_SOURCES xray_mips64.cc xray_trampoline_mips64.S)
21+
set(x86_64_SOURCES
22+
xray_x86_64.cc
23+
xray_trampoline_x86_64.S)
24+
25+
set(arm_SOURCES
26+
xray_arm.cc
27+
xray_trampoline_arm.S)
28+
29+
set(armhf_SOURCES
30+
${arm_SOURCES})
31+
32+
set(aarch64_SOURCES
33+
xray_AArch64.cc
34+
xray_trampoline_AArch64.S)
35+
36+
set(mips_SOURCES
37+
xray_mips.cc
38+
xray_trampoline_mips.S)
39+
40+
set(mipsel_SOURCES
41+
xray_mips.cc
42+
xray_trampoline_mips.S)
43+
44+
set(mips64_SOURCES
45+
xray_mips64.cc
46+
xray_trampoline_mips64.S)
47+
48+
set(mips64el_SOURCES
49+
xray_mips64.cc
50+
xray_trampoline_mips64.S)
51+
2752
set(powerpc64le_SOURCES
2853
xray_powerpc64.cc
2954
xray_trampoline_powerpc64.cc
3055
xray_trampoline_powerpc64_asm.S)
31-
set(x86_64_SOURCES xray_x86_64.cc xray_trampoline_x86_64.S)
3256

3357
# Now put it all together...
3458
include_directories(..)

lib/xray/tests/unit/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@ add_xray_unittest(XRayBufferQueueTest SOURCES
22
buffer_queue_test.cc xray_unit_test_main.cc)
33
add_xray_unittest(XRayFDRLoggingTest SOURCES
44
fdr_logging_test.cc xray_unit_test_main.cc)
5+
add_xray_unittest(XRayAllocatorTest SOURCES
6+
allocator_test.cc xray_unit_test_main.cc)
7+
add_xray_unittest(XRaySegmentedArrayTest SOURCES
8+
segmented_array_test.cc xray_unit_test_main.cc)

lib/xray/tests/unit/allocator_test.cc

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//===-- allocator_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+
14+
#include "xray_allocator.h"
15+
#include "gtest/gtest.h"
16+
17+
namespace __xray {
18+
namespace {
19+
20+
struct TestData {
21+
s64 First;
22+
s64 Second;
23+
};
24+
25+
TEST(AllocatorTest, Construction) { Allocator<sizeof(TestData)> A(2 << 11, 0); }
26+
27+
TEST(AllocatorTest, Allocate) {
28+
Allocator<sizeof(TestData)> A(2 << 11, 0);
29+
auto B = A.Allocate();
30+
ASSERT_NE(B.Data, nullptr);
31+
}
32+
33+
TEST(AllocatorTest, OverAllocate) {
34+
Allocator<sizeof(TestData)> A(sizeof(TestData), 0);
35+
auto B1 = A.Allocate();
36+
(void)B1;
37+
auto B2 = A.Allocate();
38+
ASSERT_EQ(B2.Data, nullptr);
39+
}
40+
41+
} // namespace
42+
} // namespace __xray
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
#include "xray_segmented_array.h"
2+
#include "gtest/gtest.h"
3+
4+
namespace __xray {
5+
namespace {
6+
7+
struct TestData {
8+
s64 First;
9+
s64 Second;
10+
11+
// Need a constructor for emplace operations.
12+
TestData(s64 F, s64 S) : First(F), Second(S) {}
13+
};
14+
15+
TEST(SegmentedArrayTest, Construction) {
16+
Array<TestData> Data;
17+
(void)Data;
18+
}
19+
20+
TEST(SegmentedArrayTest, ConstructWithAllocator) {
21+
using AllocatorType = typename Array<TestData>::AllocatorType;
22+
AllocatorType A(1 << 4, 0);
23+
Array<TestData> Data(A);
24+
(void)Data;
25+
}
26+
27+
TEST(SegmentedArrayTest, ConstructAndPopulate) {
28+
Array<TestData> data;
29+
ASSERT_NE(data.Append(TestData{0, 0}), nullptr);
30+
ASSERT_NE(data.Append(TestData{1, 1}), nullptr);
31+
ASSERT_EQ(data.size(), 2u);
32+
}
33+
34+
TEST(SegmentedArrayTest, ConstructPopulateAndLookup) {
35+
Array<TestData> data;
36+
ASSERT_NE(data.Append(TestData{0, 1}), nullptr);
37+
ASSERT_EQ(data.size(), 1u);
38+
ASSERT_EQ(data[0].First, 0);
39+
ASSERT_EQ(data[0].Second, 1);
40+
}
41+
42+
TEST(SegmentedArrayTest, PopulateWithMoreElements) {
43+
Array<TestData> data;
44+
static const auto kMaxElements = 100u;
45+
for (auto I = 0u; I < kMaxElements; ++I) {
46+
ASSERT_NE(data.Append(TestData{I, I + 1}), nullptr);
47+
}
48+
ASSERT_EQ(data.size(), kMaxElements);
49+
for (auto I = 0u; I < kMaxElements; ++I) {
50+
ASSERT_EQ(data[I].First, I);
51+
ASSERT_EQ(data[I].Second, I + 1);
52+
}
53+
}
54+
55+
TEST(SegmentedArrayTest, AppendEmplace) {
56+
Array<TestData> data;
57+
ASSERT_NE(data.AppendEmplace(1, 1), nullptr);
58+
ASSERT_EQ(data[0].First, 1);
59+
ASSERT_EQ(data[0].Second, 1);
60+
}
61+
62+
TEST(SegmentedArrayTest, AppendAndTrim) {
63+
Array<TestData> data;
64+
ASSERT_NE(data.AppendEmplace(1, 1), nullptr);
65+
ASSERT_EQ(data.size(), 1u);
66+
data.trim(1);
67+
ASSERT_EQ(data.size(), 0u);
68+
ASSERT_TRUE(data.empty());
69+
}
70+
71+
TEST(SegmentedArrayTest, IteratorAdvance) {
72+
Array<TestData> data;
73+
ASSERT_TRUE(data.empty());
74+
ASSERT_EQ(data.begin(), data.end());
75+
auto I0 = data.begin();
76+
ASSERT_EQ(I0++, data.begin());
77+
ASSERT_NE(I0, data.begin());
78+
for (const auto &D : data) {
79+
(void)D;
80+
FAIL();
81+
}
82+
ASSERT_NE(data.AppendEmplace(1, 1), nullptr);
83+
ASSERT_EQ(data.size(), 1u);
84+
ASSERT_NE(data.begin(), data.end());
85+
auto &D0 = *data.begin();
86+
ASSERT_EQ(D0.First, 1);
87+
ASSERT_EQ(D0.Second, 1);
88+
}
89+
90+
TEST(SegmentedArrayTest, IteratorRetreat) {
91+
Array<TestData> data;
92+
ASSERT_TRUE(data.empty());
93+
ASSERT_EQ(data.begin(), data.end());
94+
ASSERT_NE(data.AppendEmplace(1, 1), nullptr);
95+
ASSERT_EQ(data.size(), 1u);
96+
ASSERT_NE(data.begin(), data.end());
97+
auto &D0 = *data.begin();
98+
ASSERT_EQ(D0.First, 1);
99+
ASSERT_EQ(D0.Second, 1);
100+
101+
auto I0 = data.end();
102+
ASSERT_EQ(I0--, data.end());
103+
ASSERT_NE(I0, data.end());
104+
ASSERT_EQ(I0, data.begin());
105+
ASSERT_EQ(I0->First, 1);
106+
ASSERT_EQ(I0->Second, 1);
107+
}
108+
109+
TEST(SegmentedArrayTest, IteratorTrimBehaviour) {
110+
Array<TestData> data;
111+
ASSERT_TRUE(data.empty());
112+
auto I0Begin = data.begin(), I0End = data.end();
113+
// Add enough elements in data to have more than one chunk.
114+
constexpr auto ChunkX2 = Array<TestData>::ChunkSize * 2;
115+
for (auto i = ChunkX2; i > 0u; --i) {
116+
data.Append({static_cast<s64>(i), static_cast<s64>(i)});
117+
}
118+
ASSERT_EQ(data.size(), ChunkX2);
119+
// Trim one chunk's elements worth.
120+
data.trim(ChunkX2 / 2);
121+
ASSERT_EQ(data.size(), ChunkX2 / 2);
122+
// Then trim until it's empty.
123+
data.trim(ChunkX2 / 2);
124+
ASSERT_TRUE(data.empty());
125+
126+
// Here our iterators should be the same.
127+
auto I1Begin = data.begin(), I1End = data.end();
128+
EXPECT_EQ(I0Begin, I1Begin);
129+
EXPECT_EQ(I0End, I1End);
130+
131+
// Then we ensure that adding elements back works just fine.
132+
for (auto i = ChunkX2; i > 0u; --i) {
133+
data.Append({static_cast<s64>(i), static_cast<s64>(i)});
134+
}
135+
EXPECT_EQ(data.size(), ChunkX2);
136+
}
137+
138+
} // namespace
139+
} // namespace __xray

0 commit comments

Comments
 (0)