Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / compiler-rt / lib / sanitizer_common / tests / sanitizer_stacktrace_test.cpp
blob769a9b9a9fa0a029d508065f22868b05a77a430b
1 //===-- sanitizer_stacktrace_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.
11 //===----------------------------------------------------------------------===//
13 #include "sanitizer_common/sanitizer_stacktrace.h"
15 #include <string.h>
17 #include <algorithm>
18 #include <string>
20 #include "gmock/gmock.h"
21 #include "gtest/gtest.h"
22 #include "sanitizer_common/sanitizer_common.h"
23 #include "sanitizer_internal_defs.h"
25 using testing::ContainsRegex;
26 using testing::MatchesRegex;
28 namespace __sanitizer {
30 class FastUnwindTest : public ::testing::Test {
31 protected:
32 virtual void SetUp();
33 virtual void TearDown();
35 void UnwindFast();
37 void *mapping;
38 uhwptr *fake_stack;
39 const uptr fake_stack_size = 10;
40 uhwptr start_pc;
42 uhwptr fake_bp;
43 uhwptr fake_top;
44 uhwptr fake_bottom;
45 BufferedStackTrace trace;
47 #if defined(__loongarch__) || defined(__riscv)
48 const uptr kFpOffset = 4;
49 const uptr kBpOffset = 2;
50 #else
51 const uptr kFpOffset = 2;
52 const uptr kBpOffset = 0;
53 #endif
55 private:
56 CommonFlags tmp_flags_;
59 static uptr PC(uptr idx) {
60 return (1<<20) + idx;
63 void FastUnwindTest::SetUp() {
64 size_t ps = GetPageSize();
65 mapping = MmapOrDie(2 * ps, "FastUnwindTest");
66 MprotectNoAccess((uptr)mapping, ps);
68 // Unwinder may peek 1 word down from the starting FP.
69 fake_stack = (uhwptr *)((uptr)mapping + ps + sizeof(uhwptr));
71 // Fill an array of pointers with fake fp+retaddr pairs. Frame pointers have
72 // even indices.
73 for (uptr i = 0; i + 1 < fake_stack_size; i += 2) {
74 fake_stack[i] = (uptr)&fake_stack[i + kFpOffset]; // fp
75 fake_stack[i+1] = PC(i + 1); // retaddr
77 // Mark the last fp point back up to terminate the stack trace.
78 fake_stack[RoundDownTo(fake_stack_size - 1, 2)] = (uhwptr)&fake_stack[0];
80 // Top is two slots past the end because UnwindFast subtracts two.
81 fake_top = (uhwptr)&fake_stack[fake_stack_size + kFpOffset];
82 // Bottom is one slot before the start because UnwindFast uses >.
83 fake_bottom = (uhwptr)mapping;
84 fake_bp = (uptr)&fake_stack[kBpOffset];
85 start_pc = PC(0);
87 tmp_flags_.CopyFrom(*common_flags());
90 void FastUnwindTest::TearDown() {
91 size_t ps = GetPageSize();
92 UnmapOrDie(mapping, 2 * ps);
94 // Restore default flags.
95 OverrideCommonFlags(tmp_flags_);
98 #if SANITIZER_CAN_FAST_UNWIND
100 #ifdef __sparc__
101 // Fake stacks don't meet SPARC UnwindFast requirements.
102 #define SKIP_ON_SPARC(x) DISABLED_##x
103 #else
104 #define SKIP_ON_SPARC(x) x
105 #endif
107 void FastUnwindTest::UnwindFast() {
108 trace.UnwindFast(start_pc, fake_bp, fake_top, fake_bottom, kStackTraceMax);
111 TEST_F(FastUnwindTest, SKIP_ON_SPARC(Basic)) {
112 UnwindFast();
113 // Should get all on-stack retaddrs and start_pc.
114 EXPECT_EQ(6U, trace.size);
115 EXPECT_EQ(start_pc, trace.trace[0]);
116 for (uptr i = 1; i <= 5; i++) {
117 EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
121 // From: https://github.com/google/sanitizers/issues/162
122 TEST_F(FastUnwindTest, SKIP_ON_SPARC(FramePointerLoop)) {
123 // Make one fp point to itself.
124 fake_stack[4] = (uhwptr)&fake_stack[4];
125 UnwindFast();
126 // Should get all on-stack retaddrs up to the 4th slot and start_pc.
127 EXPECT_EQ(4U, trace.size);
128 EXPECT_EQ(start_pc, trace.trace[0]);
129 for (uptr i = 1; i <= 3; i++) {
130 EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
134 TEST_F(FastUnwindTest, SKIP_ON_SPARC(MisalignedFramePointer)) {
135 // Make one fp misaligned.
136 fake_stack[4] += 3;
137 UnwindFast();
138 // Should get all on-stack retaddrs up to the 4th slot and start_pc.
139 EXPECT_EQ(4U, trace.size);
140 EXPECT_EQ(start_pc, trace.trace[0]);
141 for (uptr i = 1; i < 4U; i++) {
142 EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
146 TEST_F(FastUnwindTest, OneFrameStackTrace) {
147 trace.Unwind(start_pc, fake_bp, nullptr, true, 1);
148 EXPECT_EQ(1U, trace.size);
149 EXPECT_EQ(start_pc, trace.trace[0]);
150 EXPECT_EQ((uhwptr)&fake_stack[kBpOffset], trace.top_frame_bp);
153 TEST_F(FastUnwindTest, ZeroFramesStackTrace) {
154 trace.Unwind(start_pc, fake_bp, nullptr, true, 0);
155 EXPECT_EQ(0U, trace.size);
156 EXPECT_EQ(0U, trace.top_frame_bp);
159 TEST_F(FastUnwindTest, SKIP_ON_SPARC(FPBelowPrevFP)) {
160 // The next FP points to unreadable memory inside the stack limits, but below
161 // current FP.
162 fake_stack[0] = (uhwptr)&fake_stack[-50];
163 fake_stack[1] = PC(1);
164 UnwindFast();
165 EXPECT_EQ(2U, trace.size);
166 EXPECT_EQ(PC(0), trace.trace[0]);
167 EXPECT_EQ(PC(1), trace.trace[1]);
170 TEST_F(FastUnwindTest, SKIP_ON_SPARC(CloseToZeroFrame)) {
171 // Make one pc a NULL pointer.
172 fake_stack[5] = 0x0;
173 UnwindFast();
174 // The stack should be truncated at the NULL pointer (and not include it).
175 EXPECT_EQ(3U, trace.size);
176 EXPECT_EQ(start_pc, trace.trace[0]);
177 for (uptr i = 1; i < 3U; i++) {
178 EXPECT_EQ(PC(i*2 - 1), trace.trace[i]);
182 using StackPrintTest = FastUnwindTest;
184 TEST_F(StackPrintTest, SKIP_ON_SPARC(ContainsFullTrace)) {
185 // Override stack trace format to make testing code independent of default
186 // flag values.
187 CommonFlags flags;
188 flags.CopyFrom(*common_flags());
189 flags.stack_trace_format = "#%n %p";
190 OverrideCommonFlags(flags);
192 UnwindFast();
194 char buf[3000];
195 trace.PrintTo(buf, sizeof(buf));
196 EXPECT_THAT(std::string(buf),
197 MatchesRegex("(#[0-9]+ 0x[0-9a-f]+\n){" +
198 std::to_string(trace.size) + "}\n"));
201 TEST_F(StackPrintTest, SKIP_ON_SPARC(TruncatesContents)) {
202 UnwindFast();
204 char buf[3000];
205 uptr actual_len = trace.PrintTo(buf, sizeof(buf));
206 ASSERT_LT(actual_len, sizeof(buf));
208 char tinybuf[10];
209 trace.PrintTo(tinybuf, sizeof(tinybuf));
211 // This the truncation case.
212 ASSERT_GT(actual_len, sizeof(tinybuf));
214 // The truncated contents should be a prefix of the full contents.
215 size_t lastpos = sizeof(tinybuf) - 1;
216 EXPECT_EQ(strncmp(buf, tinybuf, lastpos), 0);
217 EXPECT_EQ(tinybuf[lastpos], '\0');
219 // Full bufffer has more contents...
220 EXPECT_NE(buf[lastpos], '\0');
223 TEST_F(StackPrintTest, SKIP_ON_SPARC(WorksWithEmptyStack)) {
224 char buf[3000];
225 trace.PrintTo(buf, sizeof(buf));
226 EXPECT_NE(strstr(buf, "<empty stack>"), nullptr);
229 TEST_F(StackPrintTest, SKIP_ON_SPARC(ReturnsCorrectLength)) {
230 UnwindFast();
232 char buf[3000];
233 uptr len = trace.PrintTo(buf, sizeof(buf));
234 size_t actual_len = strlen(buf);
235 ASSERT_LT(len, sizeof(buf));
236 EXPECT_EQ(len, actual_len);
238 char tinybuf[5];
239 len = trace.PrintTo(tinybuf, sizeof(tinybuf));
240 size_t truncated_len = strlen(tinybuf);
241 ASSERT_GE(len, sizeof(tinybuf));
242 EXPECT_EQ(len, actual_len);
243 EXPECT_EQ(truncated_len, sizeof(tinybuf) - 1);
246 TEST_F(StackPrintTest, SKIP_ON_SPARC(AcceptsZeroSize)) {
247 UnwindFast();
248 char buf[1];
249 EXPECT_GT(trace.PrintTo(buf, 0), 0u);
252 using StackPrintDeathTest = StackPrintTest;
254 TEST_F(StackPrintDeathTest, SKIP_ON_SPARC(RequiresNonNullBuffer)) {
255 UnwindFast();
256 EXPECT_DEATH(trace.PrintTo(NULL, 100), "");
259 #endif // SANITIZER_CAN_FAST_UNWIND
261 TEST(SlowUnwindTest, ShortStackTrace) {
262 BufferedStackTrace stack;
263 uptr pc = StackTrace::GetCurrentPc();
264 uptr bp = GET_CURRENT_FRAME();
265 stack.Unwind(pc, bp, nullptr, false, /*max_depth=*/0);
266 EXPECT_EQ(0U, stack.size);
267 EXPECT_EQ(0U, stack.top_frame_bp);
268 stack.Unwind(pc, bp, nullptr, false, /*max_depth=*/1);
269 EXPECT_EQ(1U, stack.size);
270 EXPECT_EQ(pc, stack.trace[0]);
271 EXPECT_EQ(bp, stack.top_frame_bp);
274 TEST(GetCurrentPc, Basic) {
275 // Test that PCs obtained via GET_CURRENT_PC()
276 // and StackTrace::GetCurrentPc() are all different
277 // and are close to the function start.
278 struct Local {
279 static NOINLINE void Test() {
280 const uptr pcs[] = {
281 (uptr)&Local::Test,
282 GET_CURRENT_PC(),
283 StackTrace::GetCurrentPc(),
284 StackTrace::GetCurrentPc(),
286 for (uptr i = 0; i < ARRAY_SIZE(pcs); i++)
287 Printf("pc%zu: 0x%zx\n", i, pcs[i]);
288 for (uptr i = 1; i < ARRAY_SIZE(pcs); i++) {
289 EXPECT_GT(pcs[i], pcs[0]);
290 EXPECT_LT(pcs[i], pcs[0] + 1000);
291 for (uptr j = 0; j < i; j++) EXPECT_NE(pcs[i], pcs[j]);
295 Local::Test();
298 // Dummy implementation. This should never be called, but is required to link
299 // non-optimized builds of this test.
300 void BufferedStackTrace::UnwindImpl(uptr pc, uptr bp, void *context,
301 bool request_fast, u32 max_depth) {
302 UNIMPLEMENTED();
305 } // namespace __sanitizer