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