Line data Source code
1 1 : /**
2 : * @file instr/instr_llvm.cpp
3 : *
4 : * @brief LLVM plugin to instrument memory writes
5 : *
6 : * This is the LLVM plugin which instruments memory allocations so as to enable
7 : * transparent rollbacks of application code state.
8 : *
9 : * SPDX-FileCopyrightText: 2008-2021 HPDCS Group <rootsim@googlegroups.com>
10 : * SPDX-License-Identifier: GPL-3.0-only
11 : */
12 : #include <instr/instr_llvm.hpp>
13 :
14 : extern "C"
15 : {
16 : #include <log/log.h>
17 : }
18 :
19 : #include "llvm/Analysis/TargetLibraryInfo.h"
20 : #include "llvm/Config/llvm-config.h"
21 : #include "llvm/IR/DerivedTypes.h"
22 : #include "llvm/IR/GlobalValue.h"
23 : #include "llvm/IR/InstIterator.h"
24 : #include "llvm/IR/IntrinsicInst.h"
25 : #include "llvm/IR/LegacyPassManager.h"
26 : #include "llvm/Pass.h"
27 : #include "llvm/Support/Casting.h"
28 : #include "llvm/Transforms/IPO/PassManagerBuilder.h"
29 : #include "llvm/Transforms/Utils/Cloning.h"
30 :
31 : using namespace llvm;
32 :
33 : namespace {
34 0 : enum instrumentation_stats {
35 : TRACED_STORE,
36 : TRACED_MEMSET,
37 : TRACED_MEMCPY,
38 : TRACED_CALL,
39 : TRACED_ATOMIC,
40 : TRACED_UNKNOWN,
41 : INSTRUMENTATION_STATS_COUNT
42 : };
43 :
44 : static bool nullTermArrayContains(const char *const *arr,
45 : const char *val, size_t val_len)
46 : {
47 : while (*arr) {
48 : if (!strncmp(*arr, val, val_len) && !(*arr)[val_len])
49 : return true;
50 : ++arr;
51 : }
52 : return false;
53 : }
54 :
55 : static bool isToSubstitute(StringRef s)
56 : {
57 : return nullTermArrayContains(instr_cfg.to_substitute,
58 : s.data(), s.size());
59 : }
60 :
61 : static bool isToIgnore(StringRef s)
62 : {
63 : return nullTermArrayContains(instr_cfg.to_ignore,
64 : s.data(), s.size());
65 : }
66 :
67 : static Function *CloneFunctionStub(Function &F, const char *suffix)
68 : {
69 : std::string NewFName = F.getName().str() + suffix;
70 : Function *NewF = Function::Create(
71 : cast<FunctionType>(F.getValueType()),
72 : F.getLinkage(),
73 : F.getAddressSpace(),
74 : NewFName,
75 : F.getParent()
76 : );
77 : NewF->copyAttributesFrom(&F);
78 : return NewF;
79 : }
80 :
81 : static void CloneFunctionIntoAndMap(Function *NewF, const Function &F,
82 : ValueToValueMapTy &VMap, const char *suffix)
83 : {
84 : Function::arg_iterator DestI = NewF->arg_begin();
85 : for (const Argument &I : F.args()) {
86 : DestI->setName(I.getName());
87 : VMap[&I] = DestI++;
88 : }
89 :
90 : SmallVector<ReturnInst *, 8> Returns;
91 : CloneFunctionInto(NewF, &F, VMap, true, Returns, suffix);
92 :
93 : for (const Argument &I : F.args()) {
94 : VMap.erase(&I);
95 : }
96 : // XXX: solves a LLVM bug but removes debug info from clones
97 : NewF->setSubprogram(nullptr);
98 : }
99 :
100 : class RootsimCC: public ModulePass {
101 : public:
102 : RootsimCC() : ModulePass(ID){}
103 :
104 : virtual void getAnalysisUsage(AnalysisUsage &AU) const
105 : {
106 : AU.addRequired<TargetLibraryInfoWrapperPass>();
107 : }
108 :
109 : bool runOnModule(Module &M)
110 : {
111 : #if LOG_LEVEL <= LOG_DEBUG
112 : errs() << "Instrumenting module " << raw_ostream::CYAN <<
113 : M.getName() << "\n";
114 : errs().resetColor();
115 : #endif
116 :
117 : ValueToValueMapTy VMap;
118 : std::vector<Function *> F_vec;
119 : for (Function &F : M) {
120 : if ((isSystemSide(F) && !isToSubstitute(F.getName()))
121 : || isToIgnore(F.getName())) {
122 : #if LOG_LEVEL <= LOG_DEBUG
123 : errs() << "Ignoring function " << F.getName()
124 : << "\n";
125 : #endif
126 : } else {
127 : #if LOG_LEVEL <= LOG_DEBUG
128 : errs() << "Found function " << F.getName()
129 : << "\n";
130 : #endif
131 : F_vec.push_back(&F);
132 : }
133 : }
134 :
135 : for (Function *F : F_vec) {
136 : if (isToSubstitute(F->getName()))
137 : VMap[F] = CloneFunctionStub(*F, instr_cfg.sub_suffix);
138 : else
139 : VMap[F] = CloneFunctionStub(*F, instr_cfg.proc_suffix);
140 : }
141 :
142 : for (Function *F : F_vec) {
143 : if (F->isDeclaration() || isToSubstitute(F->getName()))
144 : continue;
145 :
146 : Function *Cloned = cast<Function>(VMap[F]);
147 : if (Cloned == nullptr)
148 : continue;
149 : #if LOG_LEVEL <= LOG_DEBUG
150 : errs() << "Processing " << Cloned->getName() << "\n";
151 : #endif
152 : CloneFunctionIntoAndMap(Cloned, *F, VMap,
153 : instr_cfg.proc_suffix);
154 :
155 : for (BasicBlock &B : *Cloned)
156 : for (Instruction &I : B)
157 : ;// TODO: incremental instrumentation
158 : }
159 :
160 : return true;
161 : }
162 :
163 : private:
164 : static char ID;
165 :
166 : unsigned stats[INSTRUMENTATION_STATS_COUNT] = {0};
167 :
168 : bool isSystemSide(Function &F)
169 : {
170 : enum llvm::LibFunc LLF;
171 : return F.getIntrinsicID() || F.doesNotReturn() ||
172 : getAnalysis<TargetLibraryInfoWrapperPass>()
173 : #if LLVM_VERSION_MAJOR >= 10
174 : .getTLI(F.getFunction()).getLibFunc(F.getFunction(), LLF);
175 : #else
176 : .getTLI().getLibFunc(F.getFunction(), LLF);
177 : #endif
178 : }
179 :
180 : // TODO: this code has been refactored and improved but it is unused
181 : FunctionCallee InitMemtraceFunction(Module &M, const char *memtrace_name)
182 : {
183 : Type *MemtraceArgs[] = {
184 : PointerType::getUnqual(Type::getVoidTy(M.getContext())),
185 : IntegerType::get(M.getContext(), sizeof(size_t) * CHAR_BIT)
186 : };
187 :
188 : FunctionType *Fty = FunctionType::get(
189 : Type::getVoidTy(M.getContext()),
190 : MemtraceArgs,
191 : false
192 : );
193 :
194 : return M.getOrInsertFunction(memtrace_name, Fty);
195 : }
196 :
197 : // TODO: this code has been refactored and improved but it is unused
198 : void InstrumentWriteInstruction(Module &M, Instruction *TI,
199 : FunctionCallee memtrace_fnc)
200 : {
201 : if (!TI->mayWriteToMemory()) {
202 : return;
203 : }
204 :
205 : Value *args[2];
206 :
207 : if (StoreInst *SI = dyn_cast<StoreInst>(TI)) {
208 : Value *V = SI->getPointerOperand();
209 : PointerType *pointerType = cast<PointerType>(V->getType());
210 : uint64_t storeSize = M.getDataLayout()
211 : .getTypeStoreSize(pointerType->getPointerElementType());
212 : args[0] = V;
213 : args[1] = ConstantInt::get(IntegerType::get(M.getContext(),
214 : sizeof(size_t) * CHAR_BIT), storeSize);
215 : ++stats[TRACED_STORE];
216 : } else if (MemSetInst *MSI = dyn_cast<MemSetInst>(TI)) {
217 : args[0] = MSI->getRawDest();
218 : args[1] = MSI->getLength();
219 : ++stats[TRACED_MEMSET];
220 : } else if (MemCpyInst *MCI = dyn_cast<MemCpyInst>(TI)) {
221 : args[0] = MCI->getRawDest();
222 : args[1] = MCI->getLength();
223 : ++stats[TRACED_STORE];
224 : } else {
225 : if (isa<CallBase>(TI)) {
226 : ++stats[TRACED_CALL];
227 : } else if (TI->isAtomic()) {
228 : errs() << "Encountered an atomic non-store instruction in function "
229 : << TI->getParent()->getParent()->getName() << "\n";
230 : ++stats[TRACED_ATOMIC];
231 : } else {
232 : errs() << "Encountered an unknown memory writing instruction in function "
233 : << TI->getParent()->getParent()->getName() << "\n";
234 : ++stats[TRACED_UNKNOWN];
235 : }
236 : return;
237 : }
238 :
239 : CallInst::Create(memtrace_fnc, args, "", TI);
240 : }
241 : };
242 : }
243 :
244 : char RootsimCC::ID = 0;
245 :
246 0 : static void loadPass(
247 : const PassManagerBuilder &Builder,
248 : llvm::legacy::PassManagerBase &PM
249 : ) {
250 : (void)Builder;
251 : PM.add(new RootsimCC());
252 : }
253 :
254 0 : static RegisterStandardPasses clangtoolLoader_Ox(
255 : PassManagerBuilder::EP_ModuleOptimizerEarly,
256 : loadPass
257 : );
258 0 : static RegisterStandardPasses clangtoolLoader_O0(
259 : PassManagerBuilder::EP_EnabledOnOptLevel0,
260 : loadPass
261 : );
|