13cab2bb3Spatrick //===-- sanitizer_stackdepot_test.cpp -------------------------------------===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is a part of ThreadSanitizer/AddressSanitizer runtime.
103cab2bb3Spatrick //
113cab2bb3Spatrick //===----------------------------------------------------------------------===//
123cab2bb3Spatrick #include "sanitizer_common/sanitizer_stackdepot.h"
13d89ec533Spatrick
14*810390e3Srobert #include <atomic>
15*810390e3Srobert #include <numeric>
16*810390e3Srobert #include <regex>
17*810390e3Srobert #include <sstream>
18*810390e3Srobert #include <string>
19*810390e3Srobert #include <thread>
20*810390e3Srobert
21d89ec533Spatrick #include "gtest/gtest.h"
223cab2bb3Spatrick #include "sanitizer_common/sanitizer_internal_defs.h"
233cab2bb3Spatrick #include "sanitizer_common/sanitizer_libc.h"
243cab2bb3Spatrick
253cab2bb3Spatrick namespace __sanitizer {
263cab2bb3Spatrick
27*810390e3Srobert class StackDepotTest : public testing::Test {
28*810390e3Srobert protected:
SetUp()29*810390e3Srobert void SetUp() override { StackDepotTestOnlyUnmap(); }
TearDown()30*810390e3Srobert void TearDown() override {
31*810390e3Srobert StackDepotStats stack_depot_stats = StackDepotGetStats();
32*810390e3Srobert Printf("StackDepot: %zd ids; %zdM allocated\n",
33*810390e3Srobert stack_depot_stats.n_uniq_ids, stack_depot_stats.allocated >> 20);
34*810390e3Srobert StackDepotTestOnlyUnmap();
35*810390e3Srobert }
36*810390e3Srobert };
37*810390e3Srobert
TEST_F(StackDepotTest,Basic)38*810390e3Srobert TEST_F(StackDepotTest, Basic) {
393cab2bb3Spatrick uptr array[] = {1, 2, 3, 4, 5};
403cab2bb3Spatrick StackTrace s1(array, ARRAY_SIZE(array));
413cab2bb3Spatrick u32 i1 = StackDepotPut(s1);
423cab2bb3Spatrick StackTrace stack = StackDepotGet(i1);
433cab2bb3Spatrick EXPECT_NE(stack.trace, (uptr*)0);
443cab2bb3Spatrick EXPECT_EQ(ARRAY_SIZE(array), stack.size);
453cab2bb3Spatrick EXPECT_EQ(0, internal_memcmp(stack.trace, array, sizeof(array)));
463cab2bb3Spatrick }
473cab2bb3Spatrick
TEST_F(StackDepotTest,Absent)48*810390e3Srobert TEST_F(StackDepotTest, Absent) {
493cab2bb3Spatrick StackTrace stack = StackDepotGet((1 << 30) - 1);
503cab2bb3Spatrick EXPECT_EQ((uptr*)0, stack.trace);
513cab2bb3Spatrick }
523cab2bb3Spatrick
TEST_F(StackDepotTest,EmptyStack)53*810390e3Srobert TEST_F(StackDepotTest, EmptyStack) {
543cab2bb3Spatrick u32 i1 = StackDepotPut(StackTrace());
553cab2bb3Spatrick StackTrace stack = StackDepotGet(i1);
563cab2bb3Spatrick EXPECT_EQ((uptr*)0, stack.trace);
573cab2bb3Spatrick }
583cab2bb3Spatrick
TEST_F(StackDepotTest,ZeroId)59*810390e3Srobert TEST_F(StackDepotTest, ZeroId) {
603cab2bb3Spatrick StackTrace stack = StackDepotGet(0);
613cab2bb3Spatrick EXPECT_EQ((uptr*)0, stack.trace);
623cab2bb3Spatrick }
633cab2bb3Spatrick
TEST_F(StackDepotTest,Same)64*810390e3Srobert TEST_F(StackDepotTest, Same) {
653cab2bb3Spatrick uptr array[] = {1, 2, 3, 4, 6};
663cab2bb3Spatrick StackTrace s1(array, ARRAY_SIZE(array));
673cab2bb3Spatrick u32 i1 = StackDepotPut(s1);
683cab2bb3Spatrick u32 i2 = StackDepotPut(s1);
693cab2bb3Spatrick EXPECT_EQ(i1, i2);
703cab2bb3Spatrick StackTrace stack = StackDepotGet(i1);
713cab2bb3Spatrick EXPECT_NE(stack.trace, (uptr*)0);
723cab2bb3Spatrick EXPECT_EQ(ARRAY_SIZE(array), stack.size);
733cab2bb3Spatrick EXPECT_EQ(0, internal_memcmp(stack.trace, array, sizeof(array)));
743cab2bb3Spatrick }
753cab2bb3Spatrick
TEST_F(StackDepotTest,Several)76*810390e3Srobert TEST_F(StackDepotTest, Several) {
773cab2bb3Spatrick uptr array1[] = {1, 2, 3, 4, 7};
783cab2bb3Spatrick StackTrace s1(array1, ARRAY_SIZE(array1));
793cab2bb3Spatrick u32 i1 = StackDepotPut(s1);
803cab2bb3Spatrick uptr array2[] = {1, 2, 3, 4, 8, 9};
813cab2bb3Spatrick StackTrace s2(array2, ARRAY_SIZE(array2));
823cab2bb3Spatrick u32 i2 = StackDepotPut(s2);
833cab2bb3Spatrick EXPECT_NE(i1, i2);
843cab2bb3Spatrick }
853cab2bb3Spatrick
TEST_F(StackDepotTest,Print)86*810390e3Srobert TEST_F(StackDepotTest, Print) {
87d89ec533Spatrick uptr array1[] = {0x111, 0x222, 0x333, 0x444, 0x777};
88d89ec533Spatrick StackTrace s1(array1, ARRAY_SIZE(array1));
89d89ec533Spatrick u32 i1 = StackDepotPut(s1);
90d89ec533Spatrick uptr array2[] = {0x1111, 0x2222, 0x3333, 0x4444, 0x8888, 0x9999};
91d89ec533Spatrick StackTrace s2(array2, ARRAY_SIZE(array2));
92d89ec533Spatrick u32 i2 = StackDepotPut(s2);
93d89ec533Spatrick EXPECT_NE(i1, i2);
94*810390e3Srobert
95*810390e3Srobert auto fix_regex = [](const std::string& s) -> std::string {
96*810390e3Srobert if (!SANITIZER_WINDOWS)
97*810390e3Srobert return s;
98*810390e3Srobert return std::regex_replace(s, std::regex("\\.\\*"), ".*\\n.*");
99*810390e3Srobert };
100d89ec533Spatrick EXPECT_EXIT(
101d89ec533Spatrick (StackDepotPrintAll(), exit(0)), ::testing::ExitedWithCode(0),
102*810390e3Srobert fix_regex("Stack for id .*#0 0x1.*#1 0x2.*#2 0x3.*#3 0x4.*#4 0x7.*"));
103*810390e3Srobert EXPECT_EXIT(
104*810390e3Srobert (StackDepotPrintAll(), exit(0)), ::testing::ExitedWithCode(0),
105*810390e3Srobert fix_regex(
106*810390e3Srobert "Stack for id .*#0 0x1.*#1 0x2.*#2 0x3.*#3 0x4.*#4 0x8.*#5 0x9.*"));
107d89ec533Spatrick }
108d89ec533Spatrick
TEST_F(StackDepotTest,PrintNoLock)109*810390e3Srobert TEST_F(StackDepotTest, PrintNoLock) {
110*810390e3Srobert u32 n = 2000;
111*810390e3Srobert std::vector<u32> idx2id(n);
112*810390e3Srobert for (u32 i = 0; i < n; ++i) {
113*810390e3Srobert uptr array[] = {0x111, 0x222, i, 0x444, 0x777};
114*810390e3Srobert StackTrace s(array, ARRAY_SIZE(array));
115*810390e3Srobert idx2id[i] = StackDepotPut(s);
116*810390e3Srobert }
117*810390e3Srobert StackDepotPrintAll();
118*810390e3Srobert for (u32 i = 0; i < n; ++i) {
119*810390e3Srobert uptr array[] = {0x111, 0x222, i, 0x444, 0x777};
120*810390e3Srobert StackTrace s(array, ARRAY_SIZE(array));
121*810390e3Srobert CHECK_EQ(idx2id[i], StackDepotPut(s));
1223cab2bb3Spatrick }
1233cab2bb3Spatrick }
1243cab2bb3Spatrick
125*810390e3Srobert static struct StackDepotBenchmarkParams {
126*810390e3Srobert int UniqueStacksPerThread;
127*810390e3Srobert int RepeatPerThread;
128*810390e3Srobert int Threads;
129*810390e3Srobert bool UniqueThreads;
130*810390e3Srobert bool UseCount;
131*810390e3Srobert } params[] = {
132*810390e3Srobert // All traces are unique, very unusual.
133*810390e3Srobert {10000000, 1, 1, false, false},
134*810390e3Srobert {8000000, 1, 4, false, false},
135*810390e3Srobert {8000000, 1, 16, false, false},
136*810390e3Srobert // Probably most realistic sets.
137*810390e3Srobert {3000000, 10, 1, false, false},
138*810390e3Srobert {3000000, 10, 4, false, false},
139*810390e3Srobert {3000000, 10, 16, false, false},
140*810390e3Srobert // Update use count as msan/dfsan.
141*810390e3Srobert {3000000, 10, 1, false, true},
142*810390e3Srobert {3000000, 10, 4, false, true},
143*810390e3Srobert {3000000, 10, 16, false, true},
144*810390e3Srobert // Unrealistic, as above, but traces are unique inside of thread.
145*810390e3Srobert {4000000, 1, 4, true, false},
146*810390e3Srobert {2000000, 1, 16, true, false},
147*810390e3Srobert {2000000, 10, 4, true, false},
148*810390e3Srobert {500000, 10, 16, true, false},
149*810390e3Srobert {1500000, 10, 4, true, true},
150*810390e3Srobert {800000, 10, 16, true, true},
151*810390e3Srobert };
152*810390e3Srobert
PrintStackDepotBenchmarkParams(const testing::TestParamInfo<StackDepotBenchmarkParams> & info)153*810390e3Srobert static std::string PrintStackDepotBenchmarkParams(
154*810390e3Srobert const testing::TestParamInfo<StackDepotBenchmarkParams>& info) {
155*810390e3Srobert std::stringstream name;
156*810390e3Srobert name << info.param.UniqueStacksPerThread << "_" << info.param.RepeatPerThread
157*810390e3Srobert << "_" << info.param.Threads << (info.param.UseCount ? "_UseCount" : "")
158*810390e3Srobert << (info.param.UniqueThreads ? "_UniqueThreads" : "");
159*810390e3Srobert return name.str();
160*810390e3Srobert }
161*810390e3Srobert
162*810390e3Srobert class StackDepotBenchmark
163*810390e3Srobert : public StackDepotTest,
164*810390e3Srobert public testing::WithParamInterface<StackDepotBenchmarkParams> {};
165*810390e3Srobert
166*810390e3Srobert // Test which can be used as a simple benchmark. It's disabled to avoid slowing
167*810390e3Srobert // down check-sanitizer.
168*810390e3Srobert // Usage: Sanitizer-<ARCH>-Test --gtest_also_run_disabled_tests \
169*810390e3Srobert // '--gtest_filter=*Benchmark*'
TEST_P(StackDepotBenchmark,DISABLED_Benchmark)170*810390e3Srobert TEST_P(StackDepotBenchmark, DISABLED_Benchmark) {
171*810390e3Srobert auto Param = GetParam();
172*810390e3Srobert std::atomic<unsigned int> here = {};
173*810390e3Srobert
174*810390e3Srobert auto thread = [&](int idx) {
175*810390e3Srobert here++;
176*810390e3Srobert while (here < Param.UniqueThreads) std::this_thread::yield();
177*810390e3Srobert
178*810390e3Srobert std::vector<uptr> frames(64);
179*810390e3Srobert for (int r = 0; r < Param.RepeatPerThread; ++r) {
180*810390e3Srobert std::iota(frames.begin(), frames.end(), idx + 1);
181*810390e3Srobert for (int i = 0; i < Param.UniqueStacksPerThread; ++i) {
182*810390e3Srobert StackTrace s(frames.data(), frames.size());
183*810390e3Srobert auto h = StackDepotPut_WithHandle(s);
184*810390e3Srobert if (Param.UseCount)
185*810390e3Srobert h.inc_use_count_unsafe();
186*810390e3Srobert std::next_permutation(frames.begin(), frames.end());
187*810390e3Srobert };
188*810390e3Srobert }
189*810390e3Srobert };
190*810390e3Srobert
191*810390e3Srobert std::vector<std::thread> threads;
192*810390e3Srobert for (int i = 0; i < Param.Threads; ++i)
193*810390e3Srobert threads.emplace_back(thread, Param.UniqueThreads * i);
194*810390e3Srobert for (auto& t : threads) t.join();
195*810390e3Srobert }
196*810390e3Srobert
197*810390e3Srobert INSTANTIATE_TEST_SUITE_P(StackDepotBenchmarkSuite, StackDepotBenchmark,
198*810390e3Srobert testing::ValuesIn(params),
199*810390e3Srobert PrintStackDepotBenchmarkParams);
200*810390e3Srobert
2013cab2bb3Spatrick } // namespace __sanitizer
202