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