Computer Laboratory

Instrumentation.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 <libtesla.h>
33 
34 #include "Automaton.h"
35 #include "Debug.h"
36 #include "Instrumentation.h"
37 #include "Names.h"
38 #include "State.h"
39 #include "Transition.h"
40 
41 #include "llvm/IR/DataLayout.h"
42 #include "llvm/IR/IRBuilder.h"
43 #include "llvm/IR/Module.h"
44 #include "llvm/Support/raw_ostream.h"
45 
46 using namespace llvm;
47 
48 using std::string;
49 using std::vector;
50 
51 namespace tesla {
52 
57 llvm::Value* ConstructKey(llvm::IRBuilder<>& Builder, llvm::Module& M,
58  llvm::Function::ArgumentListType& InstrumentationArgs,
59  FunctionEvent FnEventDescription);
60 
61 BasicBlock* MatchPattern(LLVMContext& Ctx, StringRef Name, Function *Fn,
62  BasicBlock *MatchTarget, BasicBlock *NonMatchTarget,
63  Value *Val, const tesla::Argument& Pattern);
64 
65 BasicBlock *FindBlock(StringRef Name, Function& Fn) {
66  for (auto& B : Fn)
67  if (B.getName() == Name)
68  return &B;
69 
70  panic("instrumentation function '" + Fn.getName()
71  + "' has no '" + Name + "' block");
72 }
73 
74 void FnInstrumentation::AppendInstrumentation(
75  const Automaton& A, const FunctionEvent& Ev, TEquivalenceClass& Trans) {
76 
77  LLVMContext& Ctx = TargetFn->getContext();
78 
79  auto Fn = Ev.function();
80  assert(Fn.name() == TargetFn->getName());
81  assert(Ev.direction() == Dir);
82 
83  // An instrumentation function should be a linear chain of event pattern
84  // matchers and instrumentation blocks. Find the current end of this chain
85  // and insert the new instrumentation before it.
86  auto *Exit = FindBlock("exit", *InstrFn);
87  auto *Instr = BasicBlock::Create(Ctx, A.Name() + ":instr", InstrFn, Exit);
88  Exit->replaceAllUsesWith(Instr);
89 
90  // We may need to check constant values (e.g. return values).
91  size_t i = 0;
92  for (auto& InstrArg : InstrFn->getArgumentList()) {
93  const Argument& Arg = Ev.argument(i);
94  MatchPattern(Ctx, (A.Name() + ":match:arg" + Twine(i)).str(), InstrFn,
95  Instr, Exit, &InstrArg, Arg);
96 
97  // Ignore the return value, which passed as an argument to InstrFn.
98  if (++i == Ev.argument_size())
99  break;
100  }
101 
102  if (Dir == FunctionEvent::Exit && Ev.has_expectedreturnvalue()) {
103  const Argument &Arg = Ev.expectedreturnvalue();
104  Value *ReturnValue = --(InstrFn->arg_end());
105  MatchPattern(Ctx, A.Name() + ":match:retval", InstrFn,
106  Instr, Exit, ReturnValue, Arg);
107  }
108 
109  IRBuilder<> Builder(Instr);
110  Type* IntType = Type::getInt32Ty(Ctx);
111 
112  vector<Value*> Args;
113  Args.push_back(TeslaContext(A.getAssertion().context(), Ctx));
114  Args.push_back(ConstantInt::get(IntType, A.ID()));
115  Args.push_back(ConstructKey(Builder, M, InstrFn->getArgumentList(), Ev));
116  Args.push_back(Builder.CreateGlobalStringPtr(A.Name()));
117  Args.push_back(Builder.CreateGlobalStringPtr(A.String()));
118  Args.push_back(ConstructTransitions(Builder, M, Trans));
119 
120  Function *UpdateStateFn = FindStateUpdateFn(M, IntType);
121  assert(Args.size() == UpdateStateFn->arg_size());
122  Builder.CreateCall(UpdateStateFn, Args);
123  Builder.CreateBr(Exit);
124 }
125 
126 
127 StructType* KeyType(Module& M) {
128  const char Name[] = "tesla_key";
129  StructType *T = M.getTypeByName(Name);
130 
131  if (T == NULL) {
132  // A struct tesla_key is just a mask and a set of keys.
133  vector<Type*> KeyElements(TESLA_KEY_SIZE, IntPtrType(M));
134  KeyElements.push_back(Type::getInt32Ty(M.getContext()));
135  T = StructType::create(KeyElements, Name);
136  }
137 
138  return T;
139 }
140 
141 
143 Function* Printf(Module& Mod) {
144  auto& Ctx = Mod.getContext();
145 
146  FunctionType *PrintfType = FunctionType::get(
147  IntegerType::get(Ctx, 32), // return: int32
148  PointerType::getUnqual(IntegerType::get(Ctx, 8)), // format string: char*
149  true); // use varargs
150 
151  Function* Printf = cast<Function>(
152  Mod.getOrInsertFunction("printf", PrintfType));
153 
154  return Printf;
155 }
156 
158 Function* TeslaDebugging(Module& Mod) {
159  auto& Ctx = Mod.getContext();
160 
161  FunctionType *FnType = FunctionType::get(
162  IntegerType::get(Ctx, 32), // return: int32
163  PointerType::getUnqual(IntegerType::get(Ctx, 8))); // debug name: char*
164 
165  return cast<Function>(Mod.getOrInsertFunction("tesla_debugging", FnType));
166 }
167 
168 const char* Format(Type *T) {
169  if (T->isPointerTy()) return " 0x%llx";
170  if (T->isIntegerTy()) return " %d";
171  if (T->isFloatTy()) return " %f";
172  if (T->isDoubleTy()) return " %f";
173 
174  assert(false && "Unhandled arg type");
175  abort();
176 }
177 
178 } /* namespace tesla */
179 
180 
181 Function* tesla::FunctionInstrumentation(Module& Mod, const Function& Subject,
182  FunctionEvent::Direction Dir,
183  FunctionEvent::CallContext Context,
184  bool SuppressDebugInstr) {
185 
186  LLVMContext& Ctx = Mod.getContext();
187 
188  string Name = (Twine()
189  + INSTR_BASE
190  + ((Context == FunctionEvent::Callee) ? CALLEE : CALLER)
191  + ((Dir == FunctionEvent::Entry) ? ENTER : EXIT)
192  + Subject.getName()
193  ).str();
194 
195  string Tag = (Twine()
196  + "["
197  + ((Dir == FunctionEvent::Entry) ? "CAL" : "RET")
198  + ((Context == FunctionEvent::Callee) ? "E" : "R")
199  + "] "
200  ).str();
201 
202 
203  // Build the instrumentation function's type.
204  FunctionType *SubType = Subject.getFunctionType();
205  TypeVector Args(SubType->param_begin(), SubType->param_end());
206 
207  if (Dir == FunctionEvent::Exit) {
208  Type *RetType = Subject.getReturnType();
209  if (!RetType->isVoidTy())
210  Args.push_back(RetType);
211  }
212 
213  Type *Void = Type::getVoidTy(Ctx);
214  FunctionType *InstrType = FunctionType::get(Void, Args, SubType->isVarArg());
215 
216 
217  // Find (or build) the instrumentation function.
218  auto *InstrFn = dyn_cast<Function>(Mod.getOrInsertFunction(Name, InstrType));
219  assert(InstrFn != NULL);
220 
221  if (Context == FunctionEvent::Caller)
222  InstrFn->setLinkage(GlobalValue::PrivateLinkage);
223 
224  // Invariant: instrumentation blocks should have two exit blocks: one for
225  // normal termination and one for abnormal termination.
226  if (InstrFn->empty()) {
227  auto *Entry = CreateInstrPreamble(Mod, InstrFn, Tag + Subject.getName(),
228  SuppressDebugInstr);
229  auto *Exit = BasicBlock::Create(Ctx, "exit", InstrFn);
230  IRBuilder<>(Entry).CreateBr(Exit);
231  IRBuilder<>(Exit).CreateRetVoid();
232  }
233 
234  return InstrFn;
235 }
236 
237 
238 Function* tesla::StructInstrumentation(Module& Mod, StructType *Type,
239  StringRef FieldName, size_t FieldIndex,
240  bool Store, bool SuppressDebugInstr) {
241 
242  LLVMContext& Ctx = Mod.getContext();
243  StringRef StructName = Type->getName();
244 
245  string Name = (Twine()
246  + STRUCT_INSTR
247  + (Store ? STORE : LOAD)
248  + StructName
249  + "."
250  + FieldName
251  ).str();
252 
253  // Ensure that the name doesn't include a NULL terminator.
254  Name.resize(strnlen(Name.c_str(), Name.length()));
255 
256  // The function may already exist...
257  //
258  // Note: getOrInsertFunction() doesn't seem to return an existing version
259  // of this function; perhaps it's because of the private linkage?
260  Function *InstrFn = dyn_cast_or_null<Function>(Mod.getFunction(Name));
261  if (InstrFn)
262  return InstrFn;
263 
264  // The instrumentation function does not exist; we need to build it.
265  if (Type->getNumElements() <= FieldIndex)
266  panic("struct " + Type->getName() + " does not have "
267  + Twine(FieldIndex) + " elements");
268 
269  auto *FieldTy = Type->getElementType(FieldIndex);
270 
271  // Three arguments: the struct, the new value and a pointer to the field.
272  TypeVector Args;
273  Args.push_back(PointerType::getUnqual(Type));
274  Args.push_back(FieldTy);
275  Args.push_back(PointerType::getUnqual(FieldTy));
276 
277  auto *Void = Type::getVoidTy(Ctx);
278  FunctionType *InstrType = FunctionType::get(Void, Args, false);
279 
280  InstrFn = dyn_cast<Function>(Mod.getOrInsertFunction(Name, InstrType));
281  assert(InstrFn->empty());
282 
283  InstrFn->setLinkage(GlobalValue::PrivateLinkage);
284 
285  // Invariant: instrumentation blocks should have two exit blocks: one for
286  // normal termination and one for abnormal termination.
287  // Debug printf should start with [FGET] or [FSET].
288  string Tag = (Twine() + "[F" + (Store ? "SET" : "GET") + "] ").str();
289  auto *Entry =
290  CreateInstrPreamble(Mod, InstrFn, Tag + StructName + "." + FieldName,
291  SuppressDebugInstr);
292 
293  auto *Exit = BasicBlock::Create(Ctx, "exit", InstrFn);
294  IRBuilder<>(Entry).CreateBr(Exit);
295  IRBuilder<>(Exit).CreateRetVoid();
296 
297  return InstrFn;
298 }
299 
300 
301 Type* tesla::IntPtrType(Module& M) {
302  return DataLayout(&M).getIntPtrType(M.getContext());
303 }
304 
305 StructType* tesla::TransitionType(Module& M) {
306  const char Name[] = "tesla_transition";
307  StructType *T = M.getTypeByName(Name);
308  if (T)
309  return T;
310 
311  // We don't already have it; go find it!
312  LLVMContext& Ctx = M.getContext();
313  Type *IntType = Type::getInt32Ty(Ctx);
314 
315  return StructType::create(Name,
316  IntType, // from state
317  IntType, // mask on from's name
318  IntType, // to state
319  IntType, // mask on to's name
320  IntType, // explicit instruction to fork
321  NULL);
322 }
323 
324 StructType* tesla::TransitionSetType(Module& M) {
325  const char Name[] = "tesla_transitions";
326  StructType *T = M.getTypeByName(Name);
327  if (T)
328  return T;
329 
330  // We don't already have it; go find it!
331  Type *IntTy = IntegerType::get(M.getContext(), 32);
332  Type *TransStar = PointerType::getUnqual(TransitionType(M));
333 
334  return StructType::create(Name,
335  IntTy, // number of possible transitions
336  TransStar, // transition array
337  NULL);
338 }
339 
340 BasicBlock* tesla::CreateInstrPreamble(Module& Mod, Function *F,
341  const Twine& Prefix, bool SuppressDI) {
342 
343  auto& Ctx = Mod.getContext();
344  auto *Entry = BasicBlock::Create(Ctx, "entry", F);
345 
346  if (SuppressDI)
347  return Entry;
348 
349  auto *Preamble = BasicBlock::Create(Ctx, "preamble", F, Entry);
350  auto *PrintBB = BasicBlock::Create(Ctx, "printf", F);
351  IRBuilder<> Builder(Preamble);
352 
353  // Only print if TESLA_DEBUG indicates that we want output.
354  Value *DebugName = Mod.getGlobalVariable("debug_name", true);
355  if (!DebugName)
356  DebugName = Builder.CreateGlobalStringPtr("tesla.events", "debug_name");
357 
358  Value *Debugging = Builder.CreateCall(TeslaDebugging(Mod), DebugName);
359 
360  Constant *Zero = ConstantInt::get(IntegerType::get(Ctx, 32), 0);
361  Debugging = Builder.CreateICmpNE(Debugging, Zero);
362 
363  Builder.CreateCondBr(Debugging, PrintBB, Entry);
364 
365  string FormatStr(Prefix.str());
366  for (auto& Arg : F->getArgumentList()) FormatStr += Format(Arg.getType());
367  FormatStr += "\n";
368 
369  ArgVector PrintfArgs(1, Builder.CreateGlobalStringPtr(FormatStr));
370  for (auto& Arg : F->getArgumentList()) PrintfArgs.push_back(&Arg);
371 
372  IRBuilder<> PrintBuilder(PrintBB);
373  PrintBuilder.CreateCall(Printf(Mod), PrintfArgs);
374  PrintBuilder.CreateBr(Entry);
375 
376  return Entry;
377 }
378 
379 
380 Value* tesla::Cast(Value *From, StringRef Name, Type *NewType,
381  IRBuilder<>& Builder) {
382 
383  assert(From != NULL);
384  Type *CurrentType = From->getType();
385 
386  if (CurrentType == NewType)
387  return From;
388 
389  if (!CastInst::isCastable(CurrentType, NewType)) {
390  string CurrentTypeName;
391  raw_string_ostream CurrentOut(CurrentTypeName);
392  CurrentType->print(CurrentOut);
393 
394  string NewTypeName;
395  raw_string_ostream NameOut(NewTypeName);
396  NewType->print(NameOut);
397 
398  panic(
399  "Instrumentation argument "
400  + (Name.empty() ? "" : ("'" + Name + "' "))
401  + "cannot be cast from '" + CurrentOut.str()
402  + "' to '" + NameOut.str() + "'"
403  );
404  }
405 
406  if (isa<PointerType>(CurrentType))
407  return Builder.CreatePointerCast(From, NewType);
408 
409  else if (isa<IntegerType>(CurrentType))
410  return Builder.CreateIntCast(From, NewType, false);
411 
412  llvm_unreachable("failed to cast something castable");
413 }
414 
415 
416 BasicBlock* tesla::MatchPattern(LLVMContext& Ctx, StringRef Name, Function *Fn,
417  BasicBlock *MatchTarget,
418  BasicBlock *NonMatchTarget,
419  Value *Val, const tesla::Argument& Pattern) {
420 
421  if (Pattern.type() != Argument::Constant)
422  return MatchTarget;
423 
424  auto *MatchBlock = BasicBlock::Create(Ctx, Name, Fn, MatchTarget);
425  MatchTarget->replaceAllUsesWith(MatchBlock);
426 
427  IRBuilder<> M(MatchBlock);
428  Value *PatternValue = ConstantInt::getSigned(Val->getType(), Pattern.value());
429  Value *Cmp;
430 
431  switch (Pattern.constantmatch()) {
432  case Argument::Exact:
433  Cmp = M.CreateICmpEQ(Val, PatternValue);
434  break;
435 
436  case Argument::Flags:
437  // test that x contains mask: (val & pattern) == pattern
438  Cmp = M.CreateICmpEQ(M.CreateAnd(Val, PatternValue), PatternValue);
439  break;
440 
441  case Argument::Mask:
442  // test that x contains no more than mask: (val & pattern) == val
443  Cmp = M.CreateICmpEQ(M.CreateAnd(Val, PatternValue), Val);
444  break;
445  }
446 
447  M.CreateCondBr(Cmp, MatchTarget, NonMatchTarget);
448 
449  return MatchBlock;
450 }
451 
452 
453 Function* tesla::FindStateUpdateFn(Module& M, Type *IntType) {
454 
455  LLVMContext& Ctx = M.getContext();
456 
457  Type *Char = IntegerType::get(Ctx, 8);
458  Type *CharStar = PointerType::getUnqual(Char);
459  Type *TransStar = PointerType::getUnqual(TransitionSetType(M));
460  Type *KeyStar = PointerType::getUnqual(KeyType(M));
461  Type *Void = Type::getVoidTy(Ctx);
462 
463  Constant *Fn = M.getOrInsertFunction("tesla_update_state",
464  Void, // return type
465  IntType, // context
466  IntType, // class_id
467  KeyStar, // key
468  CharStar, // name
469  CharStar, // description
470  TransStar, // transitions data
471  NULL
472  );
473 
474  assert(isa<Function>(Fn));
475  return cast<Function>(Fn);
476 }
477 
478 
479 Constant* tesla::TeslaContext(AutomatonDescription::Context Context,
480  LLVMContext& Ctx) {
481  static Type *IntType = Type::getInt32Ty(Ctx);
482 
483  static auto *Global = ConstantInt::get(IntType, TESLA_CONTEXT_GLOBAL);
484  static auto *PerThread = ConstantInt::get(IntType, TESLA_CONTEXT_THREAD);
485 
486  switch (Context) {
487  case AutomatonDescription::Global: return Global;
488  case AutomatonDescription::ThreadLocal: return PerThread;
489  }
490 }
491 
492 
493 Value* tesla::ConstructKey(IRBuilder<>& Builder, Module& M,
494  Function::ArgumentListType& InstrArgs,
495  FunctionEvent FnEvent) {
496 
497  bool HaveRetVal = FnEvent.has_expectedreturnvalue();
498  const size_t TotalArgs = FnEvent.argument_size() + (HaveRetVal ? 1 : 0);
499 
500  if (InstrArgs.size() != TotalArgs)
501  panic(
502  "instrumentation for '" + FnEvent.function().name() + "' takes "
503  + Twine(InstrArgs.size())
504  + " arguments but description in manifest provides "
505  + Twine(FnEvent.argument_size())
506  + (HaveRetVal ? " and a return value" : "")
507  );
508 
509  vector<Value*> Args(TotalArgs, NULL);
510 
511  size_t i = 0;
512 
513  for (auto& InstrArg : InstrArgs) {
514  auto& Arg = (HaveRetVal && (i == (TotalArgs - 1)))
515  ? FnEvent.expectedreturnvalue()
516  : FnEvent.argument(i);
517  ++i;
518 
519  if (Arg.type() != Argument::Variable)
520  continue;
521 
522  size_t Index = Arg.index();
523 
524  assert(Index < TotalArgs);
525  Args[Index] = &InstrArg;
526  }
527 
528  return ConstructKey(Builder, M, Args);
529 }
530 
531 Value* tesla::ConstructKey(IRBuilder<>& Builder, Module& M,
532  ArrayRef<Value*> Args) {
533 
534  Value *Key = Builder.CreateAlloca(KeyType(M), 0, "key");
535  Type *IntPtrTy = IntPtrType(M);
536  Type *IntTy = Type::getInt32Ty(M.getContext());
537 
538  int KeyMask = 0;
539 
540  for (size_t i = 0; i < Args.size(); i++) {
541  Value* Arg = Args[i];
542  if (Arg == NULL)
543  continue;
544 
545  assert(i < TESLA_KEY_SIZE);
546 
547  Builder.CreateStore(
548  Cast(Arg, Twine(i - 1).str(), IntPtrTy, Builder),
549  Builder.CreateStructGEP(Key, i));
550 
551  KeyMask |= (1 << i);
552  }
553 
554  Value *Mask = Builder.CreateStructGEP(Key, TESLA_KEY_SIZE);
555  Builder.CreateStore(ConstantInt::get(IntTy, KeyMask), Mask);
556 
557  return Key;
558 }
559 
560 Constant* tesla::ConstructTransition(IRBuilder<>& Builder, llvm::Module& M,
561  const Transition& T) {
562 
563  uint32_t Flags =
564  (T.RequiresInit() ? TESLA_TRANS_INIT : 0)
565  | (T.RequiresCleanup() ? TESLA_TRANS_CLEANUP : 0);
566 
567  uint32_t Values[] = {
568  (uint32_t) T.Source().ID(),
569  T.Source().Mask(),
570  (uint32_t) T.Destination().ID(),
571  T.Destination().Mask(),
572  Flags
573  };
574 
575  Type *IntType = Type::getInt32Ty(M.getContext());
576 
577  vector<Constant*> Elements;
578  for (auto Val : Values)
579  Elements.push_back(ConstantInt::get(IntType, Val));
580 
581  return ConstantStruct::get(TransitionType(M), Elements);
582 }
583 
584 Constant* tesla::ConstructTransitions(IRBuilder<>& Builder, Module& M,
585  const TEquivalenceClass& Tr) {
586 
587  // First convert each individual transition into an llvm::Constant*.
588  vector<Constant*> Transitions;
589  for (auto T : Tr)
590  Transitions.push_back(ConstructTransition(Builder, M, *T));
591 
592  // Put them all into a global struct tesla_transitions.
593  static StructType *T = TransitionSetType(M);
594 
595  Type *IntType = Type::getInt32Ty(M.getContext());
596  Constant *Zero = ConstantInt::get(IntType, 0);
597  Constant *Zeroes[] = { Zero, Zero };
598 
599  Constant *Count = ConstantInt::get(IntType, Transitions.size());
600 
601  assert(Tr.size() > 0);
602  string Name = "transition_array_" + (*Tr.begin())->String();
603 
604  ArrayType *ArrayT = ArrayType::get(TransitionType(M), Transitions.size());
605  auto *Array = new GlobalVariable(M, ArrayT, true, GlobalValue::PrivateLinkage,
606  ConstantArray::get(ArrayT, Transitions),
607  Name);
608 
609  // Create a global variable that points to this structure.
610  Constant *ArrayPtr = ConstantExpr::getInBoundsGetElementPtr(Array, Zeroes);
611 
612  Constant *TransitionsInit = ConstantStruct::get(T, Count, ArrayPtr, NULL);
613 
614  return new GlobalVariable(M, T, true, GlobalValue::PrivateLinkage,
615  TransitionsInit, "transitions");
616 }
617