llvm.org GIT mirror llvm / 60ebb19
ThreadSanitizer, a race detector. First LLVM commit. Clang patch (flags) will follow shortly. The run-time library will also follow, but not immediately. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@150423 91177308-0d34-0410-b5e6-96231b3b80d8 Kostya Serebryany 7 years ago
7 changed file(s) with 200 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
104104 void initializePathProfilerPass(PassRegistry&);
105105 void initializeGCOVProfilerPass(PassRegistry&);
106106 void initializeAddressSanitizerPass(PassRegistry&);
107 void initializeThreadSanitizerPass(PassRegistry&);
107108 void initializeEarlyCSEPass(PassRegistry&);
108109 void initializeExpandISelPseudosPass(PassRegistry&);
109110 void initializeFindUsedTypesPass(PassRegistry&);
1616 namespace llvm {
1717
1818 class ModulePass;
19 class FunctionPass;
1920
2021 // Insert edge profiling instrumentation
2122 ModulePass *createEdgeProfilerPass();
3334
3435 // Insert AddressSanitizer (address sanity checking) instrumentation
3536 ModulePass *createAddressSanitizerPass();
37 // Insert ThreadSanitizer (race detection) instrumentation
38 FunctionPass *createThreadSanitizerPass();
3639
3740 } // End llvm namespace
3841
55 OptimalEdgeProfiling.cpp
66 PathProfiling.cpp
77 ProfilingUtils.cpp
8 ThreadSanitizer.cpp
89 )
2424 initializePathProfilerPass(Registry);
2525 initializeGCOVProfilerPass(Registry);
2626 initializeAddressSanitizerPass(Registry);
27 initializeThreadSanitizerPass(Registry);
2728 }
2829
2930 /// LLVMInitializeInstrumentation - C binding for
0 //===-- ThreadSanitizer.cpp - race detector -------------------------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file is a part of ThreadSanitizer, a race detector.
10 //
11 // The tool is under development, for the details about previous versions see
12 // http://code.google.com/p/data-race-test
13 //
14 // The instrumentation phase is quite simple:
15 // - Insert calls to run-time library before every memory access.
16 // - Optimizations may apply to avoid instrumenting some of the accesses.
17 // - Insert calls at function entry/exit.
18 // The rest is handled by the run-time library.
19 //===----------------------------------------------------------------------===//
20
21 #define DEBUG_TYPE "tsan"
22
23 #include "llvm/ADT/SmallString.h"
24 #include "llvm/ADT/SmallVector.h"
25 #include "llvm/ADT/StringExtras.h"
26 #include "llvm/Intrinsics.h"
27 #include "llvm/Function.h"
28 #include "llvm/Module.h"
29 #include "llvm/Support/Debug.h"
30 #include "llvm/Support/IRBuilder.h"
31 #include "llvm/Support/MathExtras.h"
32 #include "llvm/Target/TargetData.h"
33 #include "llvm/Transforms/Instrumentation.h"
34 #include "llvm/Transforms/Utils/ModuleUtils.h"
35 #include "llvm/Type.h"
36
37 using namespace llvm;
38
39 namespace {
40 /// ThreadSanitizer: instrument the code in module to find races.
41 struct ThreadSanitizer : public FunctionPass {
42 ThreadSanitizer();
43 bool runOnFunction(Function &F);
44 bool doInitialization(Module &M);
45 bool instrumentLoadOrStore(Instruction *I);
46 static char ID; // Pass identification, replacement for typeid.
47
48 private:
49 TargetData *TD;
50 // Callbacks to run-time library are computed in doInitialization.
51 Value *TsanFuncEntry;
52 Value *TsanFuncExit;
53 // Accesses sizes are powers of two: 1, 2, 4, 8, 16.
54 static const int kNumberOfAccessSizes = 5;
55 Value *TsanRead[kNumberOfAccessSizes];
56 Value *TsanWrite[kNumberOfAccessSizes];
57 };
58 } // namespace
59
60 char ThreadSanitizer::ID = 0;
61 INITIALIZE_PASS(ThreadSanitizer, "tsan",
62 "ThreadSanitizer: detects data races.",
63 false, false)
64
65 ThreadSanitizer::ThreadSanitizer()
66 : FunctionPass(ID),
67 TD(NULL) {
68 }
69
70 FunctionPass *llvm::createThreadSanitizerPass() {
71 return new ThreadSanitizer();
72 }
73
74 bool ThreadSanitizer::doInitialization(Module &M) {
75 TD = getAnalysisIfAvailable();
76 if (!TD)
77 return false;
78 // Always insert a call to __tsan_init into the module's CTORs.
79 IRBuilder<> IRB(M.getContext());
80 Value *TsanInit = M.getOrInsertFunction("__tsan_init",
81 IRB.getVoidTy(), NULL);
82 appendToGlobalCtors(M, cast(TsanInit), 0);
83
84 // Initialize the callbacks.
85 TsanFuncEntry = M.getOrInsertFunction("__tsan_func_entry", IRB.getVoidTy(),
86 IRB.getInt8PtrTy(), NULL);
87 TsanFuncExit = M.getOrInsertFunction("__tsan_func_exit", IRB.getVoidTy(),
88 NULL);
89 for (int i = 0; i < kNumberOfAccessSizes; ++i) {
90 SmallString<32> ReadName("__tsan_read");
91 ReadName += itostr(1 << i);
92 TsanRead[i] = M.getOrInsertFunction(ReadName, IRB.getVoidTy(),
93 IRB.getInt8PtrTy(), NULL);
94 SmallString<32> WriteName("__tsan_write");
95 WriteName += itostr(1 << i);
96 TsanWrite[i] = M.getOrInsertFunction(WriteName, IRB.getVoidTy(),
97 IRB.getInt8PtrTy(), NULL);
98 }
99 return true;
100 }
101
102 bool ThreadSanitizer::runOnFunction(Function &F) {
103 if (!TD) return false;
104 SmallVector RetVec;
105 SmallVector LoadsAndStores;
106 bool Res = false;
107 bool HasCalls = false;
108
109 // Traverse all instructions, collect loads/stores/returns, check for calls.
110 for (Function::iterator FI = F.begin(), FE = F.end();
111 FI != FE; ++FI) {
112 BasicBlock &BB = *FI;
113 for (BasicBlock::iterator BI = BB.begin(), BE = BB.end();
114 BI != BE; ++BI) {
115 if (isa(BI) || isa(BI))
116 LoadsAndStores.push_back(BI);
117 else if (isa(BI))
118 RetVec.push_back(BI);
119 else if (isa(BI) || isa(BI))
120 HasCalls = true;
121 }
122 }
123
124 // We have collected all loads and stores.
125 // FIXME: many of these accesses do not need to be checked for races
126 // (e.g. variables that do not escape, etc).
127
128 // Instrument memory accesses.
129 for (size_t i = 0, n = LoadsAndStores.size(); i < n; ++i) {
130 Res |= instrumentLoadOrStore(LoadsAndStores[i]);
131 }
132
133 // Instrument function entry/exit points if there were instrumented accesses.
134 if (Res || HasCalls) {
135 IRBuilder<> IRB(F.getEntryBlock().getFirstNonPHI());
136 Value *ReturnAddress = IRB.CreateCall(
137 Intrinsic::getDeclaration(F.getParent(), Intrinsic::returnaddress),
138 IRB.getInt32(0));
139 IRB.CreateCall(TsanFuncEntry, ReturnAddress);
140 for (size_t i = 0, n = RetVec.size(); i < n; ++i) {
141 IRBuilder<> IRBRet(RetVec[i]);
142 IRBRet.CreateCall(TsanFuncExit);
143 }
144 }
145 return Res;
146 }
147
148 bool ThreadSanitizer::instrumentLoadOrStore(Instruction *I) {
149 IRBuilder<> IRB(I);
150 bool IsWrite = isa(*I);
151 Value *Addr = IsWrite
152 ? cast(I)->getPointerOperand()
153 : cast(I)->getPointerOperand();
154 Type *OrigPtrTy = Addr->getType();
155 Type *OrigTy = cast(OrigPtrTy)->getElementType();
156 assert(OrigTy->isSized());
157 uint32_t TypeSize = TD->getTypeStoreSizeInBits(OrigTy);
158 if (TypeSize != 8 && TypeSize != 16 &&
159 TypeSize != 32 && TypeSize != 64 && TypeSize != 128) {
160 // Ignore all unusual sizes.
161 return false;
162 }
163 uint32_t Idx = CountTrailingZeros_32(TypeSize / 8);
164 assert(Idx < kNumberOfAccessSizes);
165 Value *OnAccessFunc = IsWrite ? TsanWrite[Idx] : TsanRead[Idx];
166 IRB.CreateCall(OnAccessFunc, IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()));
167 return true;
168 }
0 load_lib llvm.exp
1
2 RunLLVMTests [lsort [glob -nocomplain $srcdir/$subdir/*.{ll,c,cpp}]]
0 ; RUN: opt < %s -tsan -S | FileCheck %s
1
2 target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
3 target triple = "x86_64-unknown-linux-gnu"
4
5 define i32 @read_4_bytes(i32* %a) {
6 entry:
7 %tmp1 = load i32* %a, align 4
8 ret i32 %tmp1
9 }
10
11 ; CHECK: @llvm.global_ctors = {{.*}}@__tsan_init
12
13 ; CHECK: define i32 @read_4_bytes(i32* %a) {
14 ; CHECK: call void @__tsan_func_entry(i8* %0)
15 ; CHECK-NEXT: %1 = bitcast i32* %a to i8*
16 ; CHECK-NEXT: call void @__tsan_read4(i8* %1)
17 ; CHECK-NEXT: %tmp1 = load i32* %a, align 4
18 ; CHECK-NEXT: call void @__tsan_func_exit()
19 ; CHECK: ret i32
20
21