Computer Laboratory

Assertion.cpp
Go to the documentation of this file.
1 
2 /*
3  * Copyright (c) 2012-2013 Jonathan Anderson
4  * All rights reserved.
5  *
6  * This software was developed by SRI International and the University of
7  * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
8  * ("CTSRD"), as part of the DARPA CRASH research programme.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in the
17  * documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include "Assertion.h"
33 #include "Automaton.h"
34 #include "Debug.h"
35 #include "Instrumentation.h"
36 #include "Manifest.h"
37 #include "Names.h"
38 #include "State.h"
39 #include "Transition.h"
40 
41 #include "tesla.pb.h"
42 
43 #include <libtesla.h>
44 
45 #include "llvm/IR/Function.h"
46 #include "llvm/IR/Instructions.h"
47 #include "llvm/IR/Module.h"
48 #include "llvm/Support/raw_ostream.h"
49 
50 #include <set>
51 
52 using namespace llvm;
53 
54 using std::set;
55 using std::string;
56 
57 
58 namespace tesla {
59 
60 char AssertionSiteInstrumenter::ID = 0;
61 
62 AssertionSiteInstrumenter::~AssertionSiteInstrumenter() {
63  ::google::protobuf::ShutdownProtobufLibrary();
64 }
65 
66 bool AssertionSiteInstrumenter::runOnModule(Module &M) {
67  // If this module doesn't declare any assertions, just carry on.
68  AssertFn = M.getFunction(INLINE_ASSERTION);
69  if (!AssertFn)
70  return false;
71 
72  // Find all calls to TESLA assertion pseudo-function (before we modify IR).
73  set<CallInst*> AssertCalls;
74  for (auto I = AssertFn->use_begin(); I != AssertFn->use_end(); ++I)
75  AssertCalls.insert(cast<CallInst>(*I));
76 
77  return ConvertAssertions(AssertCalls, M);
78 }
79 
80 
81 bool AssertionSiteInstrumenter::ConvertAssertions(
82  set<CallInst*>& AssertCalls, Module& Mod) {
83 
84  // Things we'll need later, common to all assertions.
85  Type *IntPtrTy = IntPtrType(Mod);
86 
87  // Convert each assertion into appropriate instrumentation.
88  for (auto *Assert : AssertCalls) {
89  // Prepare to insert new code in place of the assertion.
90  IRBuilder<> Builder(Assert);
91 
92  Location Loc;
93  ParseAssertionLocation(&Loc, Assert);
94 
95  auto *A = M.FindAutomaton(Loc);
96  assert(A);
97 
98  // Implement the assertion instrumentation.
99  const NowTransition *NowTrans = NULL;
100  Function *InstrFn;
101 
102  for (auto EquivClass : *A) {
103  auto *Head = *EquivClass.begin();
104  NowTrans = dyn_cast<NowTransition>(Head);
105  if (!NowTrans)
106  continue;
107 
108  if (NowTrans->Location() != Loc)
109  panic("automaton '" + ShortName(Loc)
110  + "' contains NOW event with location '"
111  + ShortName(NowTrans->Location()) + "'");
112 
113  if (!(InstrFn = CreateInstrumentation(*A, EquivClass, Mod)))
114  panic("error instrumenting NOW event");
115 
116  break;
117  }
118 
119  if (!NowTrans)
120  panic("automaton '" + ShortName(Loc) + "' contains no NOW event");
121 
122  // Record named values that might be passed to instrumentation, such as
123  // function parameters and StoreInst results in the current BasicBlock.
124  std::map<string,Value*> ValuesInScope;
125  BasicBlock *Block = Assert->getParent();
126  Function *Fn = Block->getParent();
127 
128  for (llvm::Argument& A : Fn->getArgumentList())
129  ValuesInScope[A.getName()] = &A;
130 
131  for (auto& B : *Fn)
132  for (Instruction& I : B) {
133  if (&I == Assert)
134  break;
135 
136  if (StoreInst *Store = dyn_cast<StoreInst>(&I)) {
137  Value *V = Store->getPointerOperand();
138  if (V->hasName())
139  ValuesInScope[V->getName()] = V;
140  }
141  }
142 
143  // Find the arguments to the relevant 'now' instrumentation function.
144  const AutomatonDescription& Descrip = A->getAssertion();
145  size_t ArgCount = Descrip.argument_size();
146  assert(ArgCount == InstrFn->getArgumentList().size());
147 
148  std::vector<Value*> Args(ArgCount, NULL);
149  for (const Argument& Arg : Descrip.argument()) {
150  Value *V = ValuesInScope[Arg.name()];
151  if (V == NULL)
152  panic("assertion references non-existent variable '" + Arg.name()
153  + "'; was it defined under '#ifdef TESLA'?");
154 
155  // Find the pointer to the variable we care about, which is either a
156  // stack-allocated store target or else, if there is no address-taking,
157  // the variable we already have must be the pointer.
158  Value *Ptr;
159  for (auto i = V->use_begin(); i != V->use_end(); i++) {
160  auto *Store = dyn_cast<StoreInst>(*i);
161  if (!Store)
162  continue;
163 
164  Ptr = Store->getPointerOperand();
165 
166  if (V == Store->getValueOperand())
167  assert(isa<AllocaInst>(Ptr));
168  }
169 
170  // If LLVM hasn't taken the address of our variable, it's because the
171  // variable *is* an address.
172  if (!Ptr)
173  Ptr = V;
174 
175  Value *Val = Builder.CreateLoad(Ptr, "intrumentation_" + Arg.name());
176  Args[Arg.index()] = Cast(Val, Arg.name(), IntPtrTy, Builder);
177  }
178 
179  Builder.CreateCall(InstrFn, Args);
180 
181  // Delete the call to the assertion pseudo-function.
182  Assert->removeFromParent();
183  delete Assert;
184 
185  }
186 
187  AssertFn->removeFromParent();
188  delete AssertFn;
189 
190  return true;
191 }
192 
193 
194 Function* AssertionSiteInstrumenter::CreateInstrumentation(
195  const Automaton& A, const TEquivalenceClass& Eq, Module& M) {
196 
197  const AutomatonDescription& Descrip = A.getAssertion();
198  LLVMContext& Ctx = M.getContext();
199 
200  const size_t ArgCount = Descrip.argument_size();
201 
202  Type *Void = Type::getVoidTy(Ctx);
203  Type *IntPtrTy = IntPtrType(M);
204 
205  // NOW events only take arguments of type intptr_t.
206  std::vector<Type*> ArgTypes(ArgCount, IntPtrTy);
207  FunctionType *FnType = FunctionType::get(Void, ArgTypes, false);
208  string Name = (ASSERTION_REACHED + "_" + Twine(A.ID())).str();
209 
210  Function *InstrFn = dyn_cast<Function>(M.getOrInsertFunction(Name, FnType));
211  assert(InstrFn != NULL && "instrumentation function not a Function!");
212 
213  string Message = ("[NOW] automaton " + Twine(A.ID())).str();
214 
215  BasicBlock *Instr = CreateInstrPreamble(M, InstrFn, Message,
216  SuppressDebugInstr);
217  IRBuilder<> Builder(Instr);
218 
219  Type *IntType = Type::getInt32Ty(Ctx);
220 
221  // The arguments to the function should be passed straight through to
222  // libtesla via a tesla_key: they are already in the right order.
223  std::vector<Value*> InstrArgs;
224  for (Value& Arg : InstrFn->getArgumentList()) InstrArgs.push_back(&Arg);
225 
226  std::vector<Value*> Args;
227  Args.push_back(TeslaContext(A.getAssertion().context(), Ctx));
228  Args.push_back(ConstantInt::get(IntType, A.ID()));
229  Args.push_back(ConstructKey(Builder, M, InstrArgs));
230  Args.push_back(Builder.CreateGlobalStringPtr(A.Name()));
231  Args.push_back(Builder.CreateGlobalStringPtr(A.String()));
232  Args.push_back(ConstructTransitions(Builder, M, Eq));
233 
234  Function *UpdateStateFn = FindStateUpdateFn(M, IntType);
235  assert(Args.size() == UpdateStateFn->arg_size());
236  Builder.CreateCall(UpdateStateFn, Args);
237 
238  auto Exit = BasicBlock::Create(Ctx, "exit", InstrFn);
239  IRBuilder<>(Exit).CreateRetVoid();
240 
241  Builder.CreateBr(Exit);
242 
243  return InstrFn;
244 }
245 
246 
247 void AssertionSiteInstrumenter::ParseAssertionLocation(
248  Location *Loc, CallInst *Call) {
249 
250  assert(Call->getCalledFunction()->getName() == INLINE_ASSERTION);
251 
252  if (Call->getNumArgOperands() < 3)
253  panic("TESLA assertion must have at least 3 arguments");
254 
255  // The filename should be a global variable.
256  GlobalVariable *NameVar =
257  dyn_cast<GlobalVariable>(Call->getOperand(0)->stripPointerCasts());
258 
259  ConstantDataArray *A;
260  if (!NameVar ||
261  !(A = dyn_cast_or_null<ConstantDataArray>(NameVar->getInitializer()))) {
262  Call->dump();
263  panic("unable to parse filename from TESLA assertion");
264  }
265 
266  *Loc->mutable_filename() = A->getAsString();
267 
268 
269  // The line and counter values should be constant integers.
270  ConstantInt *Line = dyn_cast<ConstantInt>(Call->getOperand(1));
271  if (!Line) {
272  Call->getOperand(1)->dump();
273  panic("assertion line must be a constant int");
274  }
275 
276  Loc->set_line(Line->getLimitedValue(INT_MAX));
277 
278  ConstantInt *Count = dyn_cast<ConstantInt>(Call->getOperand(2));
279  if (!Count) {
280  Call->getOperand(2)->dump();
281  panic("assertion count must be a constant int");
282  }
283 
284  Loc->set_counter(Count->getLimitedValue(INT_MAX));
285 }
286 
287 }
288