brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.5 KiB · cf42294 Raw
306 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This file is a part of ThreadSanitizer/AddressSanitizer runtime.10//11//===----------------------------------------------------------------------===//12 13#include "sanitizer_common/sanitizer_stacktrace.h"14 15#include <string.h>16 17#include <algorithm>18#include <string>19 20#include "gmock/gmock.h"21#include "gtest/gtest.h"22#include "sanitizer_common/sanitizer_common.h"23#include "sanitizer_internal_defs.h"24 25using testing::ContainsRegex;26using testing::MatchesRegex;27 28namespace __sanitizer {29 30class FastUnwindTest : public ::testing::Test {31 protected:32  virtual void SetUp();33  virtual void TearDown();34 35  void UnwindFast();36 37  void *mapping;38  uhwptr *fake_stack;39  const uptr fake_stack_size = 10;40  uhwptr start_pc;41 42  uhwptr fake_bp;43  uhwptr fake_top;44  uhwptr fake_bottom;45  BufferedStackTrace trace;46 47#if defined(__loongarch__) || defined(__riscv)48  const uptr kFpOffset = 4;49  const uptr kBpOffset = 2;50#else51  const uptr kFpOffset = 2;52  const uptr kBpOffset = 0;53#endif54 55 private:56  CommonFlags tmp_flags_;57};58 59static uptr PC(uptr idx) {60  return (1<<20) + idx;61}62 63void FastUnwindTest::SetUp() {64  size_t ps = GetPageSize();65  mapping = MmapOrDie(2 * ps, "FastUnwindTest");66  MprotectNoAccess((uptr)mapping, ps);67 68  // Unwinder may peek 1 word down from the starting FP.69  fake_stack = (uhwptr *)((uptr)mapping + ps + sizeof(uhwptr));70 71  // Fill an array of pointers with fake fp+retaddr pairs.  Frame pointers have72  // even indices.73  for (uptr i = 0; i + 1 < fake_stack_size; i += 2) {74    fake_stack[i] = (uptr)&fake_stack[i + kFpOffset];  // fp75    fake_stack[i+1] = PC(i + 1); // retaddr76  }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];79 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);86 87  tmp_flags_.CopyFrom(*common_flags());88}89 90void FastUnwindTest::TearDown() {91  size_t ps = GetPageSize();92  UnmapOrDie(mapping, 2 * ps);93 94  // Restore default flags.95  OverrideCommonFlags(tmp_flags_);96}97 98#if SANITIZER_CAN_FAST_UNWIND99 100#ifdef __sparc__101// Fake stacks don't meet SPARC UnwindFast requirements.102#define SKIP_ON_SPARC(x) DISABLED_##x103#else104#define SKIP_ON_SPARC(x) x105#endif106 107void FastUnwindTest::UnwindFast() {108  trace.UnwindFast(start_pc, fake_bp, fake_top, fake_bottom, kStackTraceMax);109}110 111TEST_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]);118  }119}120 121// From: https://github.com/google/sanitizers/issues/162122TEST_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]);131  }132}133 134TEST_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]);143  }144}145 146TEST_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);151}152 153TEST_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);157}158 159TEST_F(FastUnwindTest, SKIP_ON_SPARC(FPBelowPrevFP)) {160  // The next FP points to unreadable memory inside the stack limits, but below161  // 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]);168}169 170TEST_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]);179  }180}181 182using StackPrintTest = FastUnwindTest;183 184TEST_F(StackPrintTest, SKIP_ON_SPARC(ContainsFullTrace)) {185  // Override stack trace format to make testing code independent of default186  // flag values.187  CommonFlags flags;188  flags.CopyFrom(*common_flags());189  flags.stack_trace_format = "#%n %p";190  OverrideCommonFlags(flags);191 192  UnwindFast();193 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"));199}200 201TEST_F(StackPrintTest, SKIP_ON_SPARC(TruncatesContents)) {202  UnwindFast();203 204  char buf[3000];205  uptr actual_len = trace.PrintTo(buf, sizeof(buf));206  ASSERT_LT(actual_len, sizeof(buf));207 208  char tinybuf[10];209  trace.PrintTo(tinybuf, sizeof(tinybuf));210 211  // This the truncation case.212  ASSERT_GT(actual_len, sizeof(tinybuf));213 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');218 219  // Full bufffer has more contents...220  EXPECT_NE(buf[lastpos], '\0');221}222 223TEST_F(StackPrintTest, SKIP_ON_SPARC(WorksWithEmptyStack)) {224  char buf[3000];225  trace.PrintTo(buf, sizeof(buf));226  EXPECT_NE(strstr(buf, "<empty stack>"), nullptr);227}228 229TEST_F(StackPrintTest, SKIP_ON_SPARC(ReturnsCorrectLength)) {230  UnwindFast();231 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);237 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);244}245 246TEST_F(StackPrintTest, SKIP_ON_SPARC(AcceptsZeroSize)) {247  UnwindFast();248  char buf[1];249  EXPECT_GT(trace.PrintTo(buf, 0), 0u);250}251 252using StackPrintDeathTest = StackPrintTest;253 254TEST_F(StackPrintDeathTest, SKIP_ON_SPARC(RequiresNonNullBuffer)) {255  UnwindFast();256  EXPECT_DEATH(trace.PrintTo(NULL, 100), "");257}258 259#endif // SANITIZER_CAN_FAST_UNWIND260 261TEST(SlowUnwindTest, ShortStackTrace) {262  UNINITIALIZED 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);272}273 274TEST(GetCurrentPc, Basic) {275  // Test that PCs obtained via GET_CURRENT_PC()276  // and StackTrace::GetCurrentPc() are all different277  // 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(),285      };286      for (uptr i = 0; i < ARRAY_SIZE(pcs); i++)287        Printf("pc%zu: %p\n", i, (void *)(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]);292      }293    }294  };295  Local::Test();296}297 298// Dummy implementation. This should never be called, but is required to link299// non-optimized builds of this test.300void BufferedStackTrace::UnwindImpl(uptr pc, uptr bp, void *context,301                                    bool request_fast, u32 max_depth) {302  UNIMPLEMENTED();303}304 305}  // namespace __sanitizer306