1 //===-- sanitizer_stackdepot_test.cpp -------------------------------------===// 2 // 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 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file is a part of ThreadSanitizer/AddressSanitizer runtime. 10 // 11 //===----------------------------------------------------------------------===// 12 #include "sanitizer_common/sanitizer_stackdepot.h" 13 14 #include <atomic> 15 #include <numeric> 16 #include <regex> 17 #include <sstream> 18 #include <string> 19 #include <thread> 20 21 #include "gtest/gtest.h" 22 #include "sanitizer_common/sanitizer_internal_defs.h" 23 #include "sanitizer_common/sanitizer_libc.h" 24 25 namespace __sanitizer { 26 27 TEST(SanitizerCommon, StackDepotBasic) { 28 uptr array[] = {1, 2, 3, 4, 5}; 29 StackTrace s1(array, ARRAY_SIZE(array)); 30 u32 i1 = StackDepotPut(s1); 31 StackTrace stack = StackDepotGet(i1); 32 EXPECT_NE(stack.trace, (uptr*)0); 33 EXPECT_EQ(ARRAY_SIZE(array), stack.size); 34 EXPECT_EQ(0, internal_memcmp(stack.trace, array, sizeof(array))); 35 } 36 37 TEST(SanitizerCommon, StackDepotAbsent) { 38 StackTrace stack = StackDepotGet((1 << 30) - 1); 39 EXPECT_EQ((uptr*)0, stack.trace); 40 } 41 42 TEST(SanitizerCommon, StackDepotEmptyStack) { 43 u32 i1 = StackDepotPut(StackTrace()); 44 StackTrace stack = StackDepotGet(i1); 45 EXPECT_EQ((uptr*)0, stack.trace); 46 } 47 48 TEST(SanitizerCommon, StackDepotZeroId) { 49 StackTrace stack = StackDepotGet(0); 50 EXPECT_EQ((uptr*)0, stack.trace); 51 } 52 53 TEST(SanitizerCommon, StackDepotSame) { 54 uptr array[] = {1, 2, 3, 4, 6}; 55 StackTrace s1(array, ARRAY_SIZE(array)); 56 u32 i1 = StackDepotPut(s1); 57 u32 i2 = StackDepotPut(s1); 58 EXPECT_EQ(i1, i2); 59 StackTrace stack = StackDepotGet(i1); 60 EXPECT_NE(stack.trace, (uptr*)0); 61 EXPECT_EQ(ARRAY_SIZE(array), stack.size); 62 EXPECT_EQ(0, internal_memcmp(stack.trace, array, sizeof(array))); 63 } 64 65 TEST(SanitizerCommon, StackDepotSeveral) { 66 uptr array1[] = {1, 2, 3, 4, 7}; 67 StackTrace s1(array1, ARRAY_SIZE(array1)); 68 u32 i1 = StackDepotPut(s1); 69 uptr array2[] = {1, 2, 3, 4, 8, 9}; 70 StackTrace s2(array2, ARRAY_SIZE(array2)); 71 u32 i2 = StackDepotPut(s2); 72 EXPECT_NE(i1, i2); 73 } 74 75 TEST(SanitizerCommon, StackDepotPrint) { 76 uptr array1[] = {0x111, 0x222, 0x333, 0x444, 0x777}; 77 StackTrace s1(array1, ARRAY_SIZE(array1)); 78 u32 i1 = StackDepotPut(s1); 79 uptr array2[] = {0x1111, 0x2222, 0x3333, 0x4444, 0x8888, 0x9999}; 80 StackTrace s2(array2, ARRAY_SIZE(array2)); 81 u32 i2 = StackDepotPut(s2); 82 EXPECT_NE(i1, i2); 83 84 auto fix_regex = [](const std::string& s) -> std::string { 85 if (!SANITIZER_WINDOWS) 86 return s; 87 return std::regex_replace(s, std::regex("\\.\\*"), ".*\\n.*"); 88 }; 89 EXPECT_EXIT( 90 (StackDepotPrintAll(), exit(0)), ::testing::ExitedWithCode(0), 91 fix_regex("Stack for id .*#0 0x1.*#1 0x2.*#2 0x3.*#3 0x4.*#4 0x7.*")); 92 EXPECT_EXIT( 93 (StackDepotPrintAll(), exit(0)), ::testing::ExitedWithCode(0), 94 fix_regex( 95 "Stack for id .*#0 0x1.*#1 0x2.*#2 0x3.*#3 0x4.*#4 0x8.*#5 0x9.*")); 96 } 97 98 TEST(SanitizerCommon, StackDepotPrintNoLock) { 99 u32 n = 2000; 100 std::vector<u32> idx2id(n); 101 for (u32 i = 0; i < n; ++i) { 102 uptr array[] = {0x111, 0x222, i, 0x444, 0x777}; 103 StackTrace s(array, ARRAY_SIZE(array)); 104 idx2id[i] = StackDepotPut(s); 105 } 106 StackDepotPrintAll(); 107 for (u32 i = 0; i < n; ++i) { 108 uptr array[] = {0x111, 0x222, i, 0x444, 0x777}; 109 StackTrace s(array, ARRAY_SIZE(array)); 110 CHECK_EQ(idx2id[i], StackDepotPut(s)); 111 } 112 } 113 114 static struct SanitizerCommonBenchmarkparams { 115 int UniqueStacksPerThread; 116 int RepeatPerThread; 117 int Threads; 118 bool UniqueThreads; 119 bool UseCount; 120 } Params[] = { 121 // All traces are unique, very unusual. 122 {10000000, 1, 1}, 123 {8000000, 1, 4}, 124 {8000000, 1, 16}, 125 // Probably most realistic sets. 126 {3000000, 10, 1}, 127 {3000000, 10, 4}, 128 {3000000, 10, 16}, 129 // Update use count as msan/dfsan. 130 {3000000, 10, 1, false, true}, 131 {3000000, 10, 4, false, true}, 132 {3000000, 10, 16, false, true}, 133 // Unrealistic, as above, but traces are unique inside of thread. 134 {4000000, 1, 4, true}, 135 {2000000, 1, 16, true}, 136 {2000000, 10, 4, true}, 137 {500000, 10, 16, true}, 138 {1500000, 10, 4, true, true}, 139 {800000, 10, 16, true, true}, 140 }; 141 142 std::string PrintSanitizerCommonBenchmarkparams( 143 const testing::TestParamInfo<SanitizerCommonBenchmarkparams>& info) { 144 std::stringstream name; 145 name << info.param.UniqueStacksPerThread << "_" << info.param.RepeatPerThread 146 << "_" << info.param.Threads << (info.param.UseCount ? "_UseCount" : "") 147 << (info.param.UniqueThreads ? "_UniqueThreads" : ""); 148 return name.str(); 149 } 150 151 class SanitizerCommonBenchmark 152 : public testing::TestWithParam<SanitizerCommonBenchmarkparams> { 153 protected: 154 void Run() { 155 auto Param = GetParam(); 156 std::atomic<unsigned int> here = {}; 157 158 auto thread = [&](int idx) { 159 here++; 160 while (here < Param.UniqueThreads) std::this_thread::yield(); 161 162 std::vector<uptr> frames(64); 163 for (int r = 0; r < Param.RepeatPerThread; ++r) { 164 std::iota(frames.begin(), frames.end(), idx + 1); 165 for (int i = 0; i < Param.UniqueStacksPerThread; ++i) { 166 StackTrace s(frames.data(), frames.size()); 167 auto h = StackDepotPut_WithHandle(s); 168 if (Param.UseCount) 169 h.inc_use_count_unsafe(); 170 std::next_permutation(frames.begin(), frames.end()); 171 }; 172 } 173 }; 174 175 std::vector<std::thread> threads; 176 for (int i = 0; i < Param.Threads; ++i) 177 threads.emplace_back(thread, Param.UniqueThreads * i); 178 for (auto& t : threads) t.join(); 179 } 180 }; 181 182 // Test which can be used as a simple benchmark. It's disabled to avoid slowing 183 // down check-sanitizer. 184 // Usage: Sanitizer-<ARCH>-Test --gtest_also_run_disabled_tests \ 185 // '--gtest_filter=*Benchmark*' 186 TEST_P(SanitizerCommonBenchmark, DISABLED_Benchmark) { 187 // Call in subprocess to avoid reuse of the depot. 188 EXPECT_EXIT((Run(), exit(0)), ::testing::ExitedWithCode(0), ""); 189 } 190 191 INSTANTIATE_TEST_SUITE_P(SanitizerCommonBenchmarkSuite, 192 SanitizerCommonBenchmark, testing::ValuesIn(Params), 193 PrintSanitizerCommonBenchmarkparams); 194 195 } // namespace __sanitizer 196