brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.5 KiB · 0d354db Raw
342 lines · cpp
1//===-- tsan_trace_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 (TSan), a race detector.10//11//===----------------------------------------------------------------------===//12#include "tsan_trace.h"13 14#include <pthread.h>15 16#include "gtest/gtest.h"17#include "tsan_rtl.h"18 19#if !defined(__x86_64__)20// These tests are currently crashing on ppc64:21// https://reviews.llvm.org/D110546#302542222// due to the way we create thread contexts23// There must be some difference in thread initialization24// between normal execution and unit tests.25#  define TRACE_TEST(SUITE, NAME) TEST(SUITE, DISABLED_##NAME)26#else27#  define TRACE_TEST(SUITE, NAME) TEST(SUITE, NAME)28#endif29 30namespace __tsan {31 32// We need to run all trace tests in a new thread,33// so that the thread trace is empty initially.34template <uptr N>35struct ThreadArray {36  ThreadArray() {37    for (auto *&thr : threads) {38      thr = static_cast<ThreadState *>(39          MmapOrDie(sizeof(ThreadState), "ThreadState"));40      Tid tid = ThreadCreate(cur_thread(), 0, 0, true);41      Processor *proc = ProcCreate();42      ProcWire(proc, thr);43      ThreadStart(thr, tid, 0, ThreadType::Fiber);44    }45  }46 47  ~ThreadArray() {48    for (uptr i = 0; i < N; i++) {49      if (threads[i])50        Finish(i);51    }52  }53 54  void Finish(uptr i) {55    auto *thr = threads[i];56    threads[i] = nullptr;57    Processor *proc = thr->proc();58    ThreadFinish(thr);59    ProcUnwire(proc, thr);60    ProcDestroy(proc);61    UnmapOrDie(thr, sizeof(ThreadState));62  }63 64  ThreadState *threads[N];65  ThreadState *operator[](uptr i) { return threads[i]; }66  ThreadState *operator->() { return threads[0]; }67  operator ThreadState *() { return threads[0]; }68};69 70TRACE_TEST(Trace, RestoreAccess) {71  // A basic test with some function entry/exit events,72  // some mutex lock/unlock events and some other distracting73  // memory events.74  ThreadArray<1> thr;75  TraceFunc(thr, 0x1000);76  TraceFunc(thr, 0x1001);77  TraceMutexLock(thr, EventType::kLock, 0x4000, 0x5000, 0x6000);78  TraceMutexLock(thr, EventType::kLock, 0x4001, 0x5001, 0x6001);79  TraceMutexUnlock(thr, 0x5000);80  TraceFunc(thr);81  CHECK(TryTraceMemoryAccess(thr, 0x2001, 0x3001, 8, kAccessRead));82  TraceMutexLock(thr, EventType::kRLock, 0x4002, 0x5002, 0x6002);83  TraceFunc(thr, 0x1002);84  CHECK(TryTraceMemoryAccess(thr, 0x2000, 0x3000, 8, kAccessRead));85  // This is the access we want to find.86  // The previous one is equivalent, but RestoreStack must prefer87  // the last of the matchig accesses.88  CHECK(TryTraceMemoryAccess(thr, 0x2002, 0x3000, 8, kAccessRead));89  Lock slot_lock(&ctx->slots[static_cast<uptr>(thr->fast_state.sid())].mtx);90  ThreadRegistryLock lock1(&ctx->thread_registry);91  Lock lock2(&ctx->slot_mtx);92  Tid tid = kInvalidTid;93  VarSizeStackTrace stk;94  MutexSet mset;95  uptr tag = kExternalTagNone;96  bool res = RestoreStack(EventType::kAccessExt, thr->fast_state.sid(),97                          thr->fast_state.epoch(), 0x3000, 8, kAccessRead, &tid,98                          &stk, &mset, &tag);99  CHECK(res);100  CHECK_EQ(tid, thr->tid);101  CHECK_EQ(stk.size, 3);102  CHECK_EQ(stk.trace[0], 0x1000);103  CHECK_EQ(stk.trace[1], 0x1002);104  CHECK_EQ(stk.trace[2], 0x2002);105  CHECK_EQ(mset.Size(), 2);106  CHECK_EQ(mset.Get(0).addr, 0x5001);107  CHECK_EQ(mset.Get(0).stack_id, 0x6001);108  CHECK_EQ(mset.Get(0).write, true);109  CHECK_EQ(mset.Get(1).addr, 0x5002);110  CHECK_EQ(mset.Get(1).stack_id, 0x6002);111  CHECK_EQ(mset.Get(1).write, false);112  CHECK_EQ(tag, kExternalTagNone);113}114 115TRACE_TEST(Trace, MemoryAccessSize) {116  // Test tracing and matching of accesses of different sizes.117  struct Params {118    uptr access_size, offset, size;119    bool res;120  };121  Params tests[] = {122      {1, 0, 1, true},  {4, 0, 2, true},123      {4, 2, 2, true},  {8, 3, 1, true},124      {2, 1, 1, true},  {1, 1, 1, false},125      {8, 5, 4, false}, {4, static_cast<uptr>(-1l), 4, false},126  };127  for (auto params : tests) {128    for (int type = 0; type < 3; type++) {129      ThreadArray<1> thr;130      Printf("access_size=%zu, offset=%zu, size=%zu, res=%d, type=%d\n",131             params.access_size, params.offset, params.size, params.res, type);132      TraceFunc(thr, 0x1000);133      switch (type) {134        case 0:135          // This should emit compressed event.136          CHECK(TryTraceMemoryAccess(thr, 0x2000, 0x3000, params.access_size,137                                     kAccessRead));138          break;139        case 1:140          // This should emit full event.141          CHECK(TryTraceMemoryAccess(thr, 0x2000000, 0x3000, params.access_size,142                                     kAccessRead));143          break;144        case 2:145          TraceMemoryAccessRange(thr, 0x2000000, 0x3000, params.access_size,146                                 kAccessRead);147          break;148      }149      Lock slot_lock(&ctx->slots[static_cast<uptr>(thr->fast_state.sid())].mtx);150      ThreadRegistryLock lock1(&ctx->thread_registry);151      Lock lock2(&ctx->slot_mtx);152      Tid tid = kInvalidTid;153      VarSizeStackTrace stk;154      MutexSet mset;155      uptr tag = kExternalTagNone;156      bool res =157          RestoreStack(EventType::kAccessExt, thr->fast_state.sid(),158                       thr->fast_state.epoch(), 0x3000 + params.offset,159                       params.size, kAccessRead, &tid, &stk, &mset, &tag);160      CHECK_EQ(res, params.res);161      if (params.res) {162        CHECK_EQ(stk.size, 2);163        CHECK_EQ(stk.trace[0], 0x1000);164        CHECK_EQ(stk.trace[1], type ? 0x2000000 : 0x2000);165      }166    }167  }168}169 170TRACE_TEST(Trace, RestoreMutexLock) {171  // Check of restoration of a mutex lock event.172  ThreadArray<1> thr;173  TraceFunc(thr, 0x1000);174  TraceMutexLock(thr, EventType::kLock, 0x4000, 0x5000, 0x6000);175  TraceMutexLock(thr, EventType::kRLock, 0x4001, 0x5001, 0x6001);176  TraceMutexLock(thr, EventType::kRLock, 0x4002, 0x5001, 0x6002);177  Lock slot_lock(&ctx->slots[static_cast<uptr>(thr->fast_state.sid())].mtx);178  ThreadRegistryLock lock1(&ctx->thread_registry);179  Lock lock2(&ctx->slot_mtx);180  Tid tid = kInvalidTid;181  VarSizeStackTrace stk;182  MutexSet mset;183  uptr tag = kExternalTagNone;184  bool res = RestoreStack(EventType::kLock, thr->fast_state.sid(),185                          thr->fast_state.epoch(), 0x5001, 0, 0, &tid, &stk,186                          &mset, &tag);187  CHECK(res);188  CHECK_EQ(stk.size, 2);189  CHECK_EQ(stk.trace[0], 0x1000);190  CHECK_EQ(stk.trace[1], 0x4002);191  CHECK_EQ(mset.Size(), 2);192  CHECK_EQ(mset.Get(0).addr, 0x5000);193  CHECK_EQ(mset.Get(0).stack_id, 0x6000);194  CHECK_EQ(mset.Get(0).write, true);195  CHECK_EQ(mset.Get(1).addr, 0x5001);196  CHECK_EQ(mset.Get(1).stack_id, 0x6001);197  CHECK_EQ(mset.Get(1).write, false);198}199 200TRACE_TEST(Trace, MultiPart) {201  // Check replay of a trace with multiple parts.202  ThreadArray<1> thr;203  FuncEntry(thr, 0x1000);204  FuncEntry(thr, 0x2000);205  MutexPreLock(thr, 0x4000, 0x5000, 0);206  MutexPostLock(thr, 0x4000, 0x5000, 0);207  MutexPreLock(thr, 0x4000, 0x5000, 0);208  MutexPostLock(thr, 0x4000, 0x5000, 0);209  const uptr kEvents = 3 * sizeof(TracePart) / sizeof(Event);210  for (uptr i = 0; i < kEvents; i++) {211    FuncEntry(thr, 0x3000);212    MutexPreLock(thr, 0x4002, 0x5002, 0);213    MutexPostLock(thr, 0x4002, 0x5002, 0);214    MutexUnlock(thr, 0x4003, 0x5002, 0);215    FuncExit(thr);216  }217  FuncEntry(thr, 0x4000);218  TraceMutexLock(thr, EventType::kRLock, 0x4001, 0x5001, 0x6001);219  CHECK(TryTraceMemoryAccess(thr, 0x2002, 0x3000, 8, kAccessRead));220  Lock slot_lock(&ctx->slots[static_cast<uptr>(thr->fast_state.sid())].mtx);221  ThreadRegistryLock lock1(&ctx->thread_registry);222  Lock lock2(&ctx->slot_mtx);223  Tid tid = kInvalidTid;224  VarSizeStackTrace stk;225  MutexSet mset;226  uptr tag = kExternalTagNone;227  bool res = RestoreStack(EventType::kAccessExt, thr->fast_state.sid(),228                          thr->fast_state.epoch(), 0x3000, 8, kAccessRead, &tid,229                          &stk, &mset, &tag);230  CHECK(res);231  CHECK_EQ(tid, thr->tid);232  CHECK_EQ(stk.size, 4);233  CHECK_EQ(stk.trace[0], 0x1000);234  CHECK_EQ(stk.trace[1], 0x2000);235  CHECK_EQ(stk.trace[2], 0x4000);236  CHECK_EQ(stk.trace[3], 0x2002);237  CHECK_EQ(mset.Size(), 2);238  CHECK_EQ(mset.Get(0).addr, 0x5000);239  CHECK_EQ(mset.Get(0).write, true);240  CHECK_EQ(mset.Get(0).count, 2);241  CHECK_EQ(mset.Get(1).addr, 0x5001);242  CHECK_EQ(mset.Get(1).write, false);243  CHECK_EQ(mset.Get(1).count, 1);244}245 246TRACE_TEST(Trace, DeepSwitch) {247  ThreadArray<1> thr;248  for (int i = 0; i < 2000; i++) {249    FuncEntry(thr, 0x1000);250    const uptr kEvents = sizeof(TracePart) / sizeof(Event);251    for (uptr i = 0; i < kEvents; i++) {252      TraceMutexLock(thr, EventType::kLock, 0x4000, 0x5000, 0x6000);253      TraceMutexUnlock(thr, 0x5000);254    }255  }256}257 258void CheckTraceState(uptr count, uptr finished, uptr excess, uptr recycle) {259  Lock l(&ctx->slot_mtx);260  Printf("CheckTraceState(%zu/%zu, %zu/%zu, %zu/%zu, %zu/%zu)\n",261         ctx->trace_part_total_allocated, count,262         ctx->trace_part_recycle_finished, finished,263         ctx->trace_part_finished_excess, excess,264         ctx->trace_part_recycle.Size(), recycle);265  CHECK_EQ(ctx->trace_part_total_allocated, count);266  CHECK_EQ(ctx->trace_part_recycle_finished, finished);267  CHECK_EQ(ctx->trace_part_finished_excess, excess);268  CHECK_EQ(ctx->trace_part_recycle.Size(), recycle);269}270 271TRACE_TEST(TraceAlloc, SingleThread) {272  TraceResetForTesting();273  auto check_thread = [&](ThreadState *thr, uptr size, uptr count,274                          uptr finished, uptr excess, uptr recycle) {275    CHECK_EQ(thr->tctx->trace.parts.Size(), size);276    CheckTraceState(count, finished, excess, recycle);277  };278  ThreadArray<2> threads;279  check_thread(threads[0], 0, 0, 0, 0, 0);280  TraceSwitchPartImpl(threads[0]);281  check_thread(threads[0], 1, 1, 0, 0, 0);282  TraceSwitchPartImpl(threads[0]);283  check_thread(threads[0], 2, 2, 0, 0, 0);284  TraceSwitchPartImpl(threads[0]);285  check_thread(threads[0], 3, 3, 0, 0, 1);286  TraceSwitchPartImpl(threads[0]);287  check_thread(threads[0], 3, 3, 0, 0, 1);288  threads.Finish(0);289  CheckTraceState(3, 3, 0, 3);290  threads.Finish(1);291  CheckTraceState(3, 3, 0, 3);292}293 294TRACE_TEST(TraceAlloc, FinishedThreadReuse) {295  TraceResetForTesting();296  constexpr uptr Hi = Trace::kFinishedThreadHi;297  constexpr uptr kThreads = 4 * Hi;298  ThreadArray<kThreads> threads;299  for (uptr i = 0; i < kThreads; i++) {300    Printf("thread %zu\n", i);301    TraceSwitchPartImpl(threads[i]);302    if (i <= Hi)303      CheckTraceState(i + 1, i, 0, i);304    else if (i <= 2 * Hi)305      CheckTraceState(Hi + 1, Hi, i - Hi, Hi);306    else307      CheckTraceState(Hi + 1, Hi, Hi, Hi);308    threads.Finish(i);309    if (i < Hi)310      CheckTraceState(i + 1, i + 1, 0, i + 1);311    else if (i < 2 * Hi)312      CheckTraceState(Hi + 1, Hi + 1, i - Hi + 1, Hi + 1);313    else314      CheckTraceState(Hi + 1, Hi + 1, Hi + 1, Hi + 1);315  }316}317 318TRACE_TEST(TraceAlloc, FinishedThreadReuse2) {319  TraceResetForTesting();320  // constexpr uptr Lo = Trace::kFinishedThreadLo;321  // constexpr uptr Hi = Trace::kFinishedThreadHi;322  constexpr uptr Min = Trace::kMinParts;323  constexpr uptr kThreads = 10;324  constexpr uptr kParts = 2 * Min;325  ThreadArray<kThreads> threads;326  for (uptr i = 0; i < kThreads; i++) {327    Printf("thread %zu\n", i);328    for (uptr j = 0; j < kParts; j++) TraceSwitchPartImpl(threads[i]);329    if (i == 0)330      CheckTraceState(Min, 0, 0, 1);331    else332      CheckTraceState(2 * Min, 0, Min, Min + 1);333    threads.Finish(i);334    if (i == 0)335      CheckTraceState(Min, Min, 0, Min);336    else337      CheckTraceState(2 * Min, 2 * Min, Min, 2 * Min);338  }339}340 341}  // namespace __tsan342