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