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-exception 6 // 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 SANITIZER_MAC || !defined(__x86_64__) 20 // These tests are currently crashing on Mac: 21 // https://reviews.llvm.org/D107911 22 // and on ppc64: https://reviews.llvm.org/D110546#3025422 23 // due to the way we create thread contexts 24 // (but they crashed on Mac with normal pthread_create as well). 25 // There must be some difference in thread initialization 26 // between normal execution and unit tests. 27 # define TRACE_TEST(SUITE, NAME) TEST(SUITE, DISABLED_##NAME) 28 #else 29 # define TRACE_TEST(SUITE, NAME) TEST(SUITE, NAME) 30 #endif 31 32 namespace __tsan { 33 34 using namespace v3; 35 36 // We need to run all trace tests in a new thread, 37 // so that the thread trace is empty initially. 38 template <uptr N> 39 struct ThreadArray { 40 ThreadArray() { 41 for (auto *&thr : threads) { 42 thr = static_cast<ThreadState *>( 43 MmapOrDie(sizeof(ThreadState), "ThreadState")); 44 Tid tid = ThreadCreate(cur_thread(), 0, 0, true); 45 Processor *proc = ProcCreate(); 46 ProcWire(proc, thr); 47 ThreadStart(thr, tid, 0, ThreadType::Fiber); 48 } 49 } 50 51 ~ThreadArray() { 52 for (uptr i = 0; i < N; i++) { 53 if (threads[i]) 54 Finish(i); 55 } 56 } 57 58 void Finish(uptr i) { 59 auto *thr = threads[i]; 60 threads[i] = nullptr; 61 Processor *proc = thr->proc(); 62 ThreadFinish(thr); 63 ProcUnwire(proc, thr); 64 ProcDestroy(proc); 65 UnmapOrDie(thr, sizeof(ThreadState)); 66 } 67 68 ThreadState *threads[N]; 69 ThreadState *operator[](uptr i) { return threads[i]; } 70 ThreadState *operator->() { return threads[0]; } 71 operator ThreadState *() { return threads[0]; } 72 }; 73 74 TRACE_TEST(Trace, RestoreAccess) { 75 // A basic test with some function entry/exit events, 76 // some mutex lock/unlock events and some other distracting 77 // memory events. 78 ThreadArray<1> thr; 79 TraceFunc(thr, 0x1000); 80 TraceFunc(thr, 0x1001); 81 TraceMutexLock(thr, v3::EventType::kLock, 0x4000, 0x5000, 0x6000); 82 TraceMutexLock(thr, v3::EventType::kLock, 0x4001, 0x5001, 0x6001); 83 TraceMutexUnlock(thr, 0x5000); 84 TraceFunc(thr); 85 CHECK(TryTraceMemoryAccess(thr, 0x2001, 0x3001, 8, kAccessRead)); 86 TraceMutexLock(thr, v3::EventType::kRLock, 0x4002, 0x5002, 0x6002); 87 TraceFunc(thr, 0x1002); 88 CHECK(TryTraceMemoryAccess(thr, 0x2000, 0x3000, 8, kAccessRead)); 89 // This is the access we want to find. 90 // The previous one is equivalent, but RestoreStack must prefer 91 // the last of the matchig accesses. 92 CHECK(TryTraceMemoryAccess(thr, 0x2002, 0x3000, 8, kAccessRead)); 93 Lock lock1(&ctx->slot_mtx); 94 ThreadRegistryLock lock2(&ctx->thread_registry); 95 VarSizeStackTrace stk; 96 MutexSet mset; 97 uptr tag = kExternalTagNone; 98 bool res = 99 RestoreStack(thr->tid, v3::EventType::kAccessExt, thr->sid, thr->epoch, 100 0x3000, 8, kAccessRead, &stk, &mset, &tag); 101 CHECK(res); 102 CHECK_EQ(stk.size, 3); 103 CHECK_EQ(stk.trace[0], 0x1000); 104 CHECK_EQ(stk.trace[1], 0x1002); 105 CHECK_EQ(stk.trace[2], 0x2002); 106 CHECK_EQ(mset.Size(), 2); 107 CHECK_EQ(mset.Get(0).addr, 0x5001); 108 CHECK_EQ(mset.Get(0).stack_id, 0x6001); 109 CHECK_EQ(mset.Get(0).write, true); 110 CHECK_EQ(mset.Get(1).addr, 0x5002); 111 CHECK_EQ(mset.Get(1).stack_id, 0x6002); 112 CHECK_EQ(mset.Get(1).write, false); 113 CHECK_EQ(tag, kExternalTagNone); 114 } 115 116 TRACE_TEST(Trace, MemoryAccessSize) { 117 // Test tracing and matching of accesses of different sizes. 118 struct Params { 119 uptr access_size, offset, size; 120 bool res; 121 }; 122 Params tests[] = { 123 {1, 0, 1, true}, {4, 0, 2, true}, 124 {4, 2, 2, true}, {8, 3, 1, true}, 125 {2, 1, 1, true}, {1, 1, 1, false}, 126 {8, 5, 4, false}, {4, static_cast<uptr>(-1l), 4, false}, 127 }; 128 for (auto params : tests) { 129 for (int type = 0; type < 3; type++) { 130 ThreadArray<1> thr; 131 Printf("access_size=%zu, offset=%zu, size=%zu, res=%d, type=%d\n", 132 params.access_size, params.offset, params.size, params.res, type); 133 TraceFunc(thr, 0x1000); 134 switch (type) { 135 case 0: 136 // This should emit compressed event. 137 CHECK(TryTraceMemoryAccess(thr, 0x2000, 0x3000, params.access_size, 138 kAccessRead)); 139 break; 140 case 1: 141 // This should emit full event. 142 CHECK(TryTraceMemoryAccess(thr, 0x2000000, 0x3000, params.access_size, 143 kAccessRead)); 144 break; 145 case 2: 146 TraceMemoryAccessRange(thr, 0x2000000, 0x3000, params.access_size, 147 kAccessRead); 148 break; 149 } 150 Lock lock1(&ctx->slot_mtx); 151 ThreadRegistryLock lock2(&ctx->thread_registry); 152 VarSizeStackTrace stk; 153 MutexSet mset; 154 uptr tag = kExternalTagNone; 155 bool res = RestoreStack(thr->tid, v3::EventType::kAccessExt, thr->sid, 156 thr->epoch, 0x3000 + params.offset, params.size, 157 kAccessRead, &stk, &mset, &tag); 158 CHECK_EQ(res, params.res); 159 if (params.res) { 160 CHECK_EQ(stk.size, 2); 161 CHECK_EQ(stk.trace[0], 0x1000); 162 CHECK_EQ(stk.trace[1], type ? 0x2000000 : 0x2000); 163 } 164 } 165 } 166 } 167 168 TRACE_TEST(Trace, RestoreMutexLock) { 169 // Check of restoration of a mutex lock event. 170 ThreadArray<1> thr; 171 TraceFunc(thr, 0x1000); 172 TraceMutexLock(thr, v3::EventType::kLock, 0x4000, 0x5000, 0x6000); 173 TraceMutexLock(thr, v3::EventType::kRLock, 0x4001, 0x5001, 0x6001); 174 TraceMutexLock(thr, v3::EventType::kRLock, 0x4002, 0x5001, 0x6002); 175 Lock lock1(&ctx->slot_mtx); 176 ThreadRegistryLock lock2(&ctx->thread_registry); 177 VarSizeStackTrace stk; 178 MutexSet mset; 179 uptr tag = kExternalTagNone; 180 bool res = RestoreStack(thr->tid, v3::EventType::kLock, thr->sid, thr->epoch, 181 0x5001, 0, 0, &stk, &mset, &tag); 182 CHECK(res); 183 CHECK_EQ(stk.size, 2); 184 CHECK_EQ(stk.trace[0], 0x1000); 185 CHECK_EQ(stk.trace[1], 0x4002); 186 CHECK_EQ(mset.Size(), 2); 187 CHECK_EQ(mset.Get(0).addr, 0x5000); 188 CHECK_EQ(mset.Get(0).stack_id, 0x6000); 189 CHECK_EQ(mset.Get(0).write, true); 190 CHECK_EQ(mset.Get(1).addr, 0x5001); 191 CHECK_EQ(mset.Get(1).stack_id, 0x6001); 192 CHECK_EQ(mset.Get(1).write, false); 193 } 194 195 TRACE_TEST(Trace, MultiPart) { 196 // Check replay of a trace with multiple parts. 197 ThreadArray<1> thr; 198 TraceFunc(thr, 0x1000); 199 TraceFunc(thr, 0x2000); 200 TraceMutexLock(thr, v3::EventType::kLock, 0x4000, 0x5000, 0x6000); 201 const uptr kEvents = 3 * sizeof(TracePart) / sizeof(v3::Event); 202 for (uptr i = 0; i < kEvents; i++) { 203 TraceFunc(thr, 0x3000); 204 TraceMutexLock(thr, v3::EventType::kLock, 0x4002, 0x5002, 0x6002); 205 TraceMutexUnlock(thr, 0x5002); 206 TraceFunc(thr); 207 } 208 TraceFunc(thr, 0x4000); 209 TraceMutexLock(thr, v3::EventType::kRLock, 0x4001, 0x5001, 0x6001); 210 CHECK(TryTraceMemoryAccess(thr, 0x2002, 0x3000, 8, kAccessRead)); 211 Lock lock1(&ctx->slot_mtx); 212 ThreadRegistryLock lock2(&ctx->thread_registry); 213 VarSizeStackTrace stk; 214 MutexSet mset; 215 uptr tag = kExternalTagNone; 216 bool res = 217 RestoreStack(thr->tid, v3::EventType::kAccessExt, thr->sid, thr->epoch, 218 0x3000, 8, kAccessRead, &stk, &mset, &tag); 219 CHECK(res); 220 CHECK_EQ(stk.size, 4); 221 CHECK_EQ(stk.trace[0], 0x1000); 222 CHECK_EQ(stk.trace[1], 0x2000); 223 CHECK_EQ(stk.trace[2], 0x4000); 224 CHECK_EQ(stk.trace[3], 0x2002); 225 CHECK_EQ(mset.Size(), 2); 226 CHECK_EQ(mset.Get(0).addr, 0x5000); 227 CHECK_EQ(mset.Get(0).stack_id, 0x6000); 228 CHECK_EQ(mset.Get(0).write, true); 229 CHECK_EQ(mset.Get(1).addr, 0x5001); 230 CHECK_EQ(mset.Get(1).stack_id, 0x6001); 231 CHECK_EQ(mset.Get(1).write, false); 232 } 233 234 } // namespace __tsan 235