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, &params);
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