1 //===-- sanitizer_stackdepot_test.cpp -------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file is a part of ThreadSanitizer/AddressSanitizer runtime.
11 //===----------------------------------------------------------------------===//
12 #include "sanitizer_common/sanitizer_stackdepot.h"
22 #include "gtest/gtest.h"
23 #include "sanitizer_common/sanitizer_internal_defs.h"
24 #include "sanitizer_common/sanitizer_libc.h"
26 namespace __sanitizer
{
28 class StackDepotTest
: public testing::Test
{
30 void SetUp() override
{ StackDepotTestOnlyUnmap(); }
31 void TearDown() override
{
32 StackDepotStats stack_depot_stats
= StackDepotGetStats();
33 Printf("StackDepot: %zd ids; %zdM allocated\n",
34 stack_depot_stats
.n_uniq_ids
, stack_depot_stats
.allocated
>> 20);
35 StackDepotTestOnlyUnmap();
39 TEST_F(StackDepotTest
, Basic
) {
40 uptr array
[] = {1, 2, 3, 4, 5};
41 StackTrace
s1(array
, ARRAY_SIZE(array
));
42 u32 i1
= StackDepotPut(s1
);
43 StackTrace stack
= StackDepotGet(i1
);
44 EXPECT_NE(stack
.trace
, (uptr
*)0);
45 EXPECT_EQ(ARRAY_SIZE(array
), stack
.size
);
46 EXPECT_EQ(0, internal_memcmp(stack
.trace
, array
, sizeof(array
)));
49 TEST_F(StackDepotTest
, Absent
) {
50 StackTrace stack
= StackDepotGet((1 << 30) - 1);
51 EXPECT_EQ((uptr
*)0, stack
.trace
);
54 TEST_F(StackDepotTest
, EmptyStack
) {
55 u32 i1
= StackDepotPut(StackTrace());
56 StackTrace stack
= StackDepotGet(i1
);
57 EXPECT_EQ((uptr
*)0, stack
.trace
);
60 TEST_F(StackDepotTest
, ZeroId
) {
61 StackTrace stack
= StackDepotGet(0);
62 EXPECT_EQ((uptr
*)0, stack
.trace
);
65 TEST_F(StackDepotTest
, Same
) {
66 uptr array
[] = {1, 2, 3, 4, 6};
67 StackTrace
s1(array
, ARRAY_SIZE(array
));
68 u32 i1
= StackDepotPut(s1
);
69 u32 i2
= StackDepotPut(s1
);
71 StackTrace stack
= StackDepotGet(i1
);
72 EXPECT_NE(stack
.trace
, (uptr
*)0);
73 EXPECT_EQ(ARRAY_SIZE(array
), stack
.size
);
74 EXPECT_EQ(0, internal_memcmp(stack
.trace
, array
, sizeof(array
)));
77 TEST_F(StackDepotTest
, Several
) {
78 uptr array1
[] = {1, 2, 3, 4, 7};
79 StackTrace
s1(array1
, ARRAY_SIZE(array1
));
80 u32 i1
= StackDepotPut(s1
);
81 uptr array2
[] = {1, 2, 3, 4, 8, 9};
82 StackTrace
s2(array2
, ARRAY_SIZE(array2
));
83 u32 i2
= StackDepotPut(s2
);
87 TEST_F(StackDepotTest
, Print
) {
88 uptr array1
[] = {0x111, 0x222, 0x333, 0x444, 0x777};
89 StackTrace
s1(array1
, ARRAY_SIZE(array1
));
90 u32 i1
= StackDepotPut(s1
);
91 uptr array2
[] = {0x1111, 0x2222, 0x3333, 0x4444, 0x8888, 0x9999};
92 StackTrace
s2(array2
, ARRAY_SIZE(array2
));
93 u32 i2
= StackDepotPut(s2
);
96 auto fix_regex
= [](const std::string
& s
) -> std::string
{
97 if (!SANITIZER_WINDOWS
)
99 return std::regex_replace(s
, std::regex("\\.\\*"), ".*\\n.*");
102 (StackDepotPrintAll(), exit(0)), ::testing::ExitedWithCode(0),
104 "Stack for id .*#0 0x0*1.*#1 0x0*2.*#2 0x0*3.*#3 0x0*4.*#4 0x0*7.*"));
105 EXPECT_EXIT((StackDepotPrintAll(), exit(0)), ::testing::ExitedWithCode(0),
106 fix_regex("Stack for id .*#0 0x0*1.*#1 0x0*2.*#2 0x0*3.*#3 "
107 "0x0*4.*#4 0x0*8.*#5 0x0*9.*"));
110 TEST_F(StackDepotTest
, PrintNoLock
) {
112 std::vector
<u32
> idx2id(n
);
113 for (u32 i
= 0; i
< n
; ++i
) {
114 uptr array
[] = {0x111, 0x222, i
, 0x444, 0x777};
115 StackTrace
s(array
, ARRAY_SIZE(array
));
116 idx2id
[i
] = StackDepotPut(s
);
118 StackDepotPrintAll();
119 for (u32 i
= 0; i
< n
; ++i
) {
120 uptr array
[] = {0x111, 0x222, i
, 0x444, 0x777};
121 StackTrace
s(array
, ARRAY_SIZE(array
));
122 CHECK_EQ(idx2id
[i
], StackDepotPut(s
));
126 static struct StackDepotBenchmarkParams
{
127 int UniqueStacksPerThread
;
133 // All traces are unique, very unusual.
134 {10000000, 1, 1, false, false},
135 {8000000, 1, 4, false, false},
136 {8000000, 1, 16, false, false},
137 // Probably most realistic sets.
138 {3000000, 10, 1, false, false},
139 {3000000, 10, 4, false, false},
140 {3000000, 10, 16, false, false},
141 // Update use count as msan/dfsan.
142 {3000000, 10, 1, false, true},
143 {3000000, 10, 4, false, true},
144 {3000000, 10, 16, false, true},
145 // Unrealistic, as above, but traces are unique inside of thread.
146 {4000000, 1, 4, true, false},
147 {2000000, 1, 16, true, false},
148 {2000000, 10, 4, true, false},
149 {500000, 10, 16, true, false},
150 {1500000, 10, 4, true, true},
151 {800000, 10, 16, true, true},
152 // Go crazy, and create too many unique stacks, such that StackStore runs
154 {1000000, 1, 128, true, true},
155 {100000000, 1, 1, true, true},
158 static std::string
PrintStackDepotBenchmarkParams(
159 const testing::TestParamInfo
<StackDepotBenchmarkParams
>& info
) {
160 std::stringstream name
;
161 name
<< info
.param
.UniqueStacksPerThread
<< "_" << info
.param
.RepeatPerThread
162 << "_" << info
.param
.Threads
<< (info
.param
.UseCount
? "_UseCount" : "")
163 << (info
.param
.UniqueThreads
? "_UniqueThreads" : "");
167 class StackDepotBenchmark
168 : public StackDepotTest
,
169 public testing::WithParamInterface
<StackDepotBenchmarkParams
> {};
171 // Test which can be used as a simple benchmark. It's disabled to avoid slowing
172 // down check-sanitizer.
173 // Usage: Sanitizer-<ARCH>-Test --gtest_also_run_disabled_tests \
174 // '--gtest_filter=*Benchmark*'
175 TEST_P(StackDepotBenchmark
, DISABLED_Benchmark
) {
176 auto Param
= GetParam();
177 std::atomic
<unsigned int> here
= {};
179 auto thread
= [&](int idx
) {
181 while (here
< Param
.UniqueThreads
) std::this_thread::yield();
183 std::vector
<uptr
> frames(64);
184 for (int r
= 0; r
< Param
.RepeatPerThread
; ++r
) {
185 std::iota(frames
.begin(), frames
.end(), idx
+ 1);
186 for (int i
= 0; i
< Param
.UniqueStacksPerThread
; ++i
) {
187 StackTrace
s(frames
.data(), frames
.size());
188 auto h
= StackDepotPut_WithHandle(s
);
190 h
.inc_use_count_unsafe();
191 std::next_permutation(frames
.begin(), frames
.end());
196 std::vector
<std::thread
> threads
;
197 for (int i
= 0; i
< Param
.Threads
; ++i
)
198 threads
.emplace_back(thread
, Param
.UniqueThreads
* i
);
199 for (auto& t
: threads
) t
.join();
202 INSTANTIATE_TEST_SUITE_P(StackDepotBenchmarkSuite
, StackDepotBenchmark
,
203 testing::ValuesIn(params
),
204 PrintStackDepotBenchmarkParams
);
206 } // namespace __sanitizer