├── .gitignore ├── Demo ├── Makefile ├── max.c ├── run-ir-mutants.sh ├── run-src-mutants.sh └── setup-environment.sh ├── Examples ├── mutateIR.sh ├── mutateSRC.sh └── test.c ├── IRMutation ├── InstrumentationLib │ ├── LlvmCoverage.c │ ├── LlvmCoverage.h │ └── build-lib.sh ├── LLVMPasses │ ├── CMakeLists.txt │ ├── Makefile │ └── Mutation │ │ ├── CMakeLists.txt │ │ ├── Makefile │ │ └── Mutate.cpp └── README ├── LICENSE ├── PythonWrappers ├── bashUtil.py ├── irCoverageClang ├── irMutationClang ├── irUtil.py ├── irVanillaClang └── mutationClang ├── README.md ├── SRCMutation ├── Makefile ├── README └── src │ └── mutator.cpp └── llvm-build.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *~ 3 | *. 4 | *.pyc 5 | *.out 6 | *.ll 7 | -------------------------------------------------------------------------------- /Demo/Makefile: -------------------------------------------------------------------------------- 1 | all: max 2 | 3 | CC := clang 4 | 5 | max: max.c 6 | ${CC} -O0 -o $@ $(shell pwd)/$< 7 | 8 | clean: 9 | rm -f max 10 | 11 | test: max 12 | ./max 13 | -------------------------------------------------------------------------------- /Demo/max.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int max(int arr[], int size) { 6 | int i; 7 | int max = INT_MIN; 8 | for (i = 0; i < size; i++) { 9 | if (arr[i] > max) { 10 | max = arr[i]; 11 | } 12 | } 13 | return max; 14 | } 15 | 16 | void testSingle() { 17 | int arr[1] = {-1}; 18 | int expected = -1; 19 | int actual = max(arr, 1); 20 | assert(expected == actual); 21 | } 22 | 23 | void testMulti() { 24 | int arr[3] = {2, 3, 1}; 25 | int expected = 3; 26 | int actual = max(arr, 3); 27 | assert(expected == actual); 28 | } 29 | 30 | int main() { 31 | testSingle(); 32 | testMulti(); 33 | printf("FINISHED\n"); 34 | } 35 | -------------------------------------------------------------------------------- /Demo/run-ir-mutants.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ASSUME SCRIPT IS RUN FROM THE DIRECTORY IT IS UNDER (Demo/) 4 | 5 | rm -rf ~/.srciror # Clean up between runs 6 | mkdir -p ~/.srciror 7 | 8 | # Generate all mutants, writes their location into .srciror/ 9 | make clean; make CC=$(pwd)/../PythonWrappers/irMutationClang 10 | 11 | # Compile each mutant out 12 | for line in $(cat ~/.srciror/bc-mutants/*); do # Read mutants in SHA identifider for this code (only one under bc-mutants/) 13 | values=`echo ${line} | cut -f6 -d: | sed 's/,/\n/g'` 14 | if [ -z "${values}" ]; then # this is a binop 15 | values=`echo ${line} | cut -f5 -d: | sed 's/,/\n/g'` 16 | fi 17 | for v in $(echo ${values}); do # Iterate through all values that should be replaced with 18 | mutant_line="max.ll+/home/awshi2/demo/max.c:$(echo ${line} | rev | cut -f2- -d: | rev):${v}" 19 | echo "${mutant_line}" > ~/.srciror/mutation_request.txt # Mutant is written into mutation_request.txt 20 | echo "RUNNING MUTANT ${mutant_line}" 21 | make clean &> /dev/null; make CC=$(pwd)/../PythonWrappers/PythonWrappers/irVanillaClang &> /dev/null 22 | timeout 10s make test &> /dev/null 23 | if [[ $? == 0 ]]; then 24 | echo "SURVIVE" 25 | else 26 | echo "KILLED" 27 | fi 28 | done 29 | done 30 | -------------------------------------------------------------------------------- /Demo/run-src-mutants.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ASSUME SCRIPT IS RUN FROM THE DIRECTORY IT IS UNDER (Demo/) 4 | 5 | mkdir -p ~/.srciror 6 | rm -rf ~/.srciror/src-mutants/ # Clean up between runs 7 | 8 | # Write out coverage for src, to speed things up 9 | echo "$(pwd)/max.c:5,6,7,8,9,10,13" > ~/.srciror/coverage 10 | 11 | # Make mutants with the Python wrapper and move into place 12 | make clean; make CC=$(pwd)/../PythonWrappers/mutationClang 13 | mkdir -p ~/.srciror/src-mutants/ 14 | mv *.mut.*.c ~/.srciror/src-mutants/ 15 | 16 | # Save original file, run tests on on each mutant 17 | cp max.c max.c.orig 18 | for m in $(ls ~/.srciror/src-mutants/); do 19 | cp ~/.srciror/src-mutants/${m} max.c 20 | echo "RUNNING MUTANT ${m}" 21 | make clean &> /dev/null; make &> /dev/null 22 | timeout 10s make test &> /dev/null 23 | if [[ $? == 0 ]]; then 24 | echo "SURVIVE" 25 | else 26 | echo "KILLED" 27 | fi 28 | done 29 | cp max.c.orig max.c # Return original file 30 | -------------------------------------------------------------------------------- /Demo/setup-environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ASSUME SCRIPT IS RUN FROM SAME DIRECTORY (Demo/) 4 | 5 | # Assume the llvm-build/ is in location above Demo/, as defined by llvm-build.sh script 6 | export SRCIROR_LLVMMutate_LIB=$(pwd)/../llvm-build/Release+Asserts/lib/LLVMMutate.so 7 | export SRCIROR_SRC_MUTATOR=$(pwd)/../SRCMutation/build/mutator 8 | export SRCIROR_LLVM_BIN=$(pwd)/../llvm-build/Release+Asserts/bin/ 9 | export SRCIROR_LLVM_INCLUDES=$(pwd)/../llvm-build/Release+Asserts/lib/clang/3.8.0/include/ 10 | -------------------------------------------------------------------------------- /Examples/mutateIR.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | CURR_DIR=$( cd $( dirname $0 ) && pwd ) 4 | 5 | # Set up paths to where compiled tools are here 6 | export SRCIROR_COVINSTRUMENTATION_LIB=$CURR_DIR/../IRMutation/InstrumentationLib/SRCIRORCoverageLib.o 7 | export SRCIROR_LLVMMutate_LIB=$CURR_DIR/../llvm-build/Release+Asserts/lib/LLVMMutate.so 8 | export SRCIROR_LLVM_BIN=$CURR_DIR/../llvm-build/Release+Asserts/bin/ 9 | 10 | # generate coverage 11 | rm -f /tmp/llvm_mutate_trace # remove any existing coverage 12 | rm -rf ~/.srciror # remove logs and results from previous runs 13 | echo "SRCIROR: Instrumenting for Coverage" 14 | python $CURR_DIR/../PythonWrappers/irCoverageClang test.c -o test 15 | # run the executable to collect coverage 16 | ./test 17 | echo "the collected coverage is under /tmp/llvm_coverage" 18 | 19 | 20 | # generate mutation opportunities 21 | echo "SRCIROR: generating mutation opportunities" 22 | python $CURR_DIR/../PythonWrappers/irMutationClang test.c 23 | echo "The generated mutants are under ~/.srciror/bc-mutants/681837891" 24 | 25 | # TODO: intersect with coverage 26 | # generate one mutant executable 27 | file_name=`cat ~/.srciror/ir-coverage/hash-map | grep "test.c" | cut -f1 -d:` 28 | mutation=`head -n1 ~/.srciror/bc-mutants/681837891 | cut -f1 -d,` 29 | echo "file name is: $file_name and mutation requested is: $mutation" 30 | echo "$file_name:$mutation" > ~/.srciror/mutation_request.txt 31 | python $CURR_DIR/../PythonWrappers/irVanillaClang test.c 32 | -------------------------------------------------------------------------------- /Examples/mutateSRC.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # generate all src mutants (assuming all lines are covered) 4 | CURR_DIR=$( cd $( dirname $0 ) && pwd ) 5 | 6 | rm -rf ~/.srciror 7 | mkdir ~/.srciror 8 | echo "$CURR_DIR/test.c:3,4,5,6,7,8,9" > ~/.srciror/coverage 9 | 10 | export SRCIROR_LLVM_BIN=$CURR_DIR/../llvm-build/Release+Asserts/bin 11 | export SRCIROR_LLVM_INCLUDES=$CURR_DIR/../llvm-build/Release+Asserts/lib/clang/3.8.0/include 12 | export SRCIROR_SRC_MUTATOR=$CURR_DIR/../SRCMutation/build/mutator 13 | python $CURR_DIR/../PythonWrappers/mutationClang $CURR_DIR/test.c -o test 14 | -------------------------------------------------------------------------------- /Examples/test.c: -------------------------------------------------------------------------------- 1 | int main(int argc) { 2 | 3 | int x = 1; 4 | if (argc > 7 ) 5 | x = x + argc; 6 | else 7 | x = argc; 8 | 9 | return x; 10 | } 11 | -------------------------------------------------------------------------------- /IRMutation/InstrumentationLib/LlvmCoverage.c: -------------------------------------------------------------------------------- 1 | #include "LlvmCoverage.h" 2 | 3 | /// Helper function for coverage instrumentation 4 | /// \param InstNum the number of the covered instruction being logged 5 | /// \param FileName the adler32 of the file name that has the covered instruction 6 | /// Note that we use the hash because passing strings to LLVM is not trivial 7 | void SRCIRORLlvmCoverage(int InstNum, uint64_t FileName) { 8 | // TODO: make the write to this file synchronized 9 | if (LlvmTrace == NULL) { 10 | LlvmTrace = fopen("/tmp/srciror_llvm_coverage", "a"); 11 | } 12 | fprintf(LlvmTrace, "%lu:%d\n", FileName, InstNum); 13 | } 14 | -------------------------------------------------------------------------------- /IRMutation/InstrumentationLib/LlvmCoverage.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | FILE * LlvmTrace; 5 | 6 | void SRCIRORLlvmCoverage(int InstNum, uint64_t FileName); 7 | -------------------------------------------------------------------------------- /IRMutation/InstrumentationLib/build-lib.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clang -c LlvmCoverage.c -o SRCIRORCoverageLib.o 3 | -------------------------------------------------------------------------------- /IRMutation/LLVMPasses/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(Utils) 2 | add_subdirectory(Instrumentation) 3 | add_subdirectory(InstCombine) 4 | add_subdirectory(Scalar) 5 | add_subdirectory(IPO) 6 | add_subdirectory(Vectorize) 7 | add_subdirectory(Hello) 8 | add_subdirectory(Mutation) 9 | add_subdirectory(ObjCARC) 10 | -------------------------------------------------------------------------------- /IRMutation/LLVMPasses/Makefile: -------------------------------------------------------------------------------- 1 | ##===- lib/Transforms/Makefile -----------------------------*- Makefile -*-===## 2 | # 3 | # The LLVM Compiler Infrastructure 4 | # 5 | # This file is distributed under the University of Illinois Open Source 6 | # License. See LICENSE.TXT for details. 7 | # 8 | ##===----------------------------------------------------------------------===## 9 | 10 | LEVEL = ../.. 11 | PARALLEL_DIRS = Utils Instrumentation Scalar InstCombine IPO Vectorize Hello Mutation 12 | 13 | include $(LEVEL)/Makefile.config 14 | 15 | # No support for plugins on windows targets 16 | ifeq ($(HOST_OS), $(filter $(HOST_OS), Cygwin MingW Minix)) 17 | PARALLEL_DIRS := $(filter-out Hello, $(PARALLEL_DIRS)) 18 | endif 19 | 20 | include $(LEVEL)/Makefile.common 21 | -------------------------------------------------------------------------------- /IRMutation/LLVMPasses/Mutation/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(WIN32 OR CYGWIN) 2 | set(LLVM_LINK_COMPONENTS Core Support) 3 | endif() 4 | 5 | add_llvm_loadable_module( LLVMMutate 6 | Mutate.cpp 7 | 8 | DEPENDS 9 | intrinsics_gen 10 | ) 11 | -------------------------------------------------------------------------------- /IRMutation/LLVMPasses/Mutation/Makefile: -------------------------------------------------------------------------------- 1 | ##===- lib/Transforms/Mutation/Makefile -----------------------*- Makefile -*-===## 2 | # 3 | # The LLVM Compiler Infrastructure 4 | # 5 | # This file is distributed under the University of Illinois Open Source 6 | # License. See LICENSE.TXT for details. 7 | # 8 | ##===----------------------------------------------------------------------===## 9 | 10 | LEVEL = ../../.. 11 | LIBRARYNAME = LLVMMutate 12 | LOADABLE_MODULE = 1 13 | USEDLIBS = 14 | 15 | include $(LEVEL)/Makefile.common 16 | 17 | -------------------------------------------------------------------------------- /IRMutation/LLVMPasses/Mutation/Mutate.cpp: -------------------------------------------------------------------------------- 1 | #define DEBUG_TYPE "mutation" 2 | #include "llvm/Pass.h" 3 | #include "llvm/ADT/Statistic.h" 4 | #include "llvm/IR/Constants.h" 5 | #include "llvm/IR/DebugInfoMetadata.h" 6 | #include "llvm/IR/Function.h" 7 | #include "llvm/IR/Instruction.h" 8 | #include "llvm/IR/Instructions.h" 9 | #include "llvm/IR/InstIterator.h" 10 | #include "llvm/IR/Module.h" 11 | #include "llvm/Support/CommandLine.h" 12 | #include "llvm/Support/raw_ostream.h" 13 | #include "llvm/Transforms/Utils/BasicBlockUtils.h" 14 | 15 | using namespace llvm; 16 | 17 | static cl::opt MutationLocation("mutation_loc", cl::desc("Specify the instruction number that you would like to mutate"), cl::value_desc("unsigned integer")); 18 | 19 | static cl::opt FileCode("file_code", cl::desc("Specify the hash of the file name we are instrumenting for coverage"), cl::value_desc("unsigned integer")); 20 | 21 | static cl::opt MutationOperandLocation("mutation_op_loc", cl::desc("Specify the instruction number that you would like to mutate"), cl::value_desc("unsigned integer")); 22 | 23 | static cl::opt MutationOp("mutation_op", cl::desc("Specify operator to mutate with e.g., 8:add, 10:sub, 12:mul"), cl::value_desc("unsigned integer")); 24 | 25 | static cl::opt MutationValue("mutation_val", cl::desc("Specify the value that will be used to replace the found integer constants"), cl::value_desc("integer")); 26 | 27 | static cl::opt IcmpPred("icmp_pred", cl::desc("Specify the icmp instruction predicate id"), cl::value_desc("integer")); 28 | 29 | static cl::opt DeleteLocation("del_loc", cl::desc("Specify the instruction number to be deleted"), cl::value_desc("integer")); 30 | 31 | STATISTIC(InstCount, "Counts number of instructions"); 32 | STATISTIC(ConstantCount, "Counts number of constants"); 33 | STATISTIC(IcmpCount, "Counts number of Icmp instructions"); 34 | STATISTIC(LogicalCount, "Counts number of logical instructions"); 35 | STATISTIC(ArithCount, "Counts number of arithmetic instructions"); 36 | 37 | namespace { 38 | struct ICRMutate : public FunctionPass { 39 | static char ID; // Pass identification, replacement for typeid 40 | ICRMutate() : FunctionPass(ID) {} 41 | 42 | virtual bool runOnFunction(Function &F) { 43 | for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) { 44 | InstCount++; 45 | findAndMutateConstantOperands(&*I, &F); 46 | } 47 | return false; 48 | } 49 | 50 | void findAndMutateConstantOperands(Instruction *I, Function* F){ 51 | // loop through operands, 52 | int counter = -1; 53 | for (User::op_iterator i = I->op_begin(), e = I->op_end(); i != e; ++i) { 54 | counter++; 55 | Value *v = *i; 56 | if (isa(v)) { 57 | if (dyn_cast(v)) { 58 | if (InstCount == MutationLocation && counter == MutationOperandLocation) { 59 | *i = ConstantInt::get(v->getType(), MutationValue); 60 | break; 61 | } 62 | } 63 | } 64 | } 65 | } 66 | 67 | }; 68 | } 69 | 70 | char ICRMutate::ID = 0; 71 | static RegisterPass X("icrmutate", "Apply Constant Replacement Mutation"); 72 | 73 | namespace { 74 | struct Coverage : public ModulePass { 75 | static char ID; // Pass identification, replacement for typeid 76 | Function *hook; 77 | CallInst *PutCall; 78 | 79 | Coverage() : ModulePass(ID) {} 80 | 81 | void walkFunction(Function *F, Module& M){ 82 | for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) { 83 | Instruction *Inst = &*I; 84 | InstCount += 1; 85 | // to avoid: PHI nodes not grouped at top of basic block! 86 | if (!isa(Inst)) { 87 | Value *Args[2]; 88 | Args[0] = ConstantInt::get(Type::getInt32Ty(F->getContext()), InstCount); 89 | Args[1] = ConstantInt::get(Type::getInt64Ty(F->getContext()), FileCode); 90 | Instruction *newInst = CallInst::Create(hook, Args, "", Inst); // Insert call to the coverage helper to record 91 | } 92 | } 93 | } 94 | 95 | virtual bool runOnModule(Module &M) { 96 | // Insert the coverage helper function into the module 97 | Constant *PutFn; 98 | PutFn = M.getOrInsertFunction("SRCIRORLlvmCoverage", Type::getVoidTy(M.getContext()), Type::getInt32Ty(M.getContext()), Type::getInt64Ty(M.getContext()), NULL); 99 | hook = cast(PutFn); 100 | for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) { 101 | Function *F = &*I; 102 | walkFunction(F, M); 103 | } 104 | return true; 105 | } 106 | 107 | }; 108 | } 109 | 110 | char Coverage::ID = 0; 111 | static RegisterPass V("coverage", "Instrument for coverage collection"); 112 | 113 | 114 | // enum Predicate {eq=1,ne=2,ugt=3,uge=4,ult=5,ule=6,sgt=7,sge=8,slt=9,sle=10}; 115 | enum InstrMut {e_arith = 60, e_icmp = 61, e_logical = 62}; 116 | 117 | namespace { 118 | struct GetICRMutationLocs : public FunctionPass { 119 | static char ID; // Pass identification, replacement for typeid 120 | GetICRMutationLocs() : FunctionPass(ID) {} 121 | 122 | virtual bool runOnFunction(Function &F) { 123 | for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) { 124 | InstCount++; 125 | findConstantOperands(&*I, &F); 126 | } 127 | return false; 128 | } 129 | 130 | void findConstantOperands(Instruction *I, Function* F){ 131 | // loop through operands, 132 | int counter = -1; 133 | for (User::op_iterator i = I->op_begin(), e = I->op_end(); i != e; ++i) { 134 | counter++; 135 | Value *v = *i; 136 | if (isa(v)) { 137 | if (ConstantInt* CI = dyn_cast(v)) { 138 | int IntegerValue = CI->getSExtValue(); // TODO: Is it okay to always sign-extend? 139 | errs() << InstCount << ":" << counter << ":" << CI->getValue() << ":"; 140 | switch (IntegerValue) { 141 | case 1: 142 | errs() << -1 << "," << 0 <<"," << 2 << ",\n"; 143 | break; 144 | case -1: 145 | errs() << 1 << "," << 0 << "," << -2 << ",\n"; 146 | break; 147 | case 0: 148 | errs() << 1 << "," << -1 << ",\n"; 149 | break; 150 | default: 151 | errs() << 0 << "," << 1 << "," << -1 << "," << IntegerValue + 1 << "," << IntegerValue - 1 << "," << IntegerValue * -1 << ",\n"; 152 | } 153 | } 154 | } 155 | } 156 | } 157 | 158 | }; 159 | } 160 | 161 | char GetICRMutationLocs::ID = 0; 162 | static RegisterPass Y("getICRMutationLocs", "Get the locations where we can apply ICR mutation."); 163 | 164 | 165 | namespace { 166 | struct GetBinaryOperators : public BasicBlockPass { // TODO: Can/should this be a FunctionPass? 167 | static char ID; // Pass identification, replacement for typeid 168 | GetBinaryOperators() : BasicBlockPass(ID) {} 169 | 170 | bool checkOpcode(unsigned opcode, int operators[], int len, int op_type) { 171 | bool done = false; 172 | for (int i = 0; i < len; i++) { 173 | if (operators[i] == opcode) { 174 | done = true; 175 | errs() << InstCount << ":" << op_type << ":"; //TODO: Do we want to output the original opcode as well? 176 | for (int j = 0; j < len; j++) { 177 | if (i != j) { 178 | errs () << operators[j] << ","; 179 | } 180 | } 181 | errs() << "\n"; 182 | break; 183 | } 184 | } 185 | return done; 186 | } 187 | 188 | void outputIcmpPredicates(int skip) { 189 | int cmpinsts[] = {CmpInst::ICMP_EQ, CmpInst::ICMP_NE, CmpInst::ICMP_UGT, CmpInst::ICMP_UGE, CmpInst::ICMP_ULT, CmpInst::ICMP_ULE, CmpInst::ICMP_SGT, CmpInst::ICMP_SGE, CmpInst::ICMP_SLT, CmpInst::ICMP_SLE}; 190 | int len = 10; 191 | for (int i = 0; i < len; i++) { 192 | if (cmpinsts[i] != skip) 193 | errs() << cmpinsts[i] << ","; 194 | } 195 | errs() << "\n"; 196 | } 197 | 198 | void handle_cmp(CmpInst *cmpInst) { 199 | outputIcmpPredicates(cmpInst->getPredicate()); 200 | } 201 | 202 | virtual bool runOnBasicBlock(BasicBlock &BB) { 203 | int arithmeticOps[] = {Instruction::Add, Instruction::Sub, Instruction::Mul, Instruction::UDiv, Instruction::SDiv, Instruction::URem, Instruction::SRem}; 204 | int floatingOps[] = {Instruction::FAdd, Instruction::FSub, Instruction::FMul, Instruction::FDiv, Instruction::FRem}; 205 | int logicalOps[] = {Instruction::And, Instruction::Or, Instruction::Xor}; 206 | 207 | for (BasicBlock::iterator DI = BB.begin(); DI != BB.end();) { 208 | Instruction *I = &*DI++; 209 | InstCount++; 210 | if (ICmpInst *cmpInst = dyn_cast(I)) { 211 | errs() << InstCount << ":" << e_icmp << ":"; 212 | handle_cmp(cmpInst); 213 | } else { 214 | unsigned opcode = I->getOpcode(); 215 | bool done = checkOpcode(opcode, arithmeticOps, sizeof(arithmeticOps) / sizeof(int), e_arith); 216 | if (!done) { 217 | done = checkOpcode(opcode, logicalOps, sizeof(logicalOps) / sizeof(int), e_logical); 218 | if (!done) { 219 | checkOpcode(opcode, floatingOps, sizeof(floatingOps) / sizeof(int), e_arith); 220 | } 221 | } 222 | } 223 | } 224 | return false; 225 | } 226 | 227 | }; 228 | } 229 | 230 | char GetBinaryOperators::ID = 0; 231 | static RegisterPass Z("getBinaryOperators", "Get the occurences of the given binary operators we want to mutate"); 232 | 233 | namespace { 234 | struct SwapBinaryOperators : public BasicBlockPass { 235 | static char ID; 236 | SwapBinaryOperators() : BasicBlockPass(ID) {} 237 | 238 | Instruction* getRequestedMutationBinaryOp(Instruction* I) { 239 | // TODO: Is there a better way to convert integer representation of opcode to BinaryOp instance? 240 | switch (MutationOp) { 241 | case Instruction::Add: 242 | return BinaryOperator::Create(Instruction::Add, I->getOperand(0), I->getOperand(1), "optimute"); 243 | case Instruction::Sub: 244 | return BinaryOperator::Create(Instruction::Sub, I->getOperand(0), I->getOperand(1), "optimute"); 245 | case Instruction::Mul: 246 | return BinaryOperator::Create(Instruction::Mul, I->getOperand(0), I->getOperand(1), "optimute"); 247 | case Instruction::UDiv: 248 | return BinaryOperator::Create(Instruction::UDiv, I->getOperand(0), I->getOperand(1), "optimute"); 249 | case Instruction::SDiv: 250 | return BinaryOperator::Create(Instruction::SDiv, I->getOperand(0), I->getOperand(1), "optimute"); 251 | case Instruction::URem: 252 | return BinaryOperator::Create(Instruction::URem, I->getOperand(0), I->getOperand(1), "optimute"); 253 | case Instruction::SRem: 254 | return BinaryOperator::Create(Instruction::SRem, I->getOperand(0), I->getOperand(1), "optimute"); 255 | 256 | case Instruction::FAdd: 257 | return BinaryOperator::Create(Instruction::FAdd, I->getOperand(0), I->getOperand(1), "optimute"); 258 | case Instruction::FSub: 259 | return BinaryOperator::Create(Instruction::FSub, I->getOperand(0), I->getOperand(1), "optimute"); 260 | case Instruction::FMul: 261 | return BinaryOperator::Create(Instruction::FMul, I->getOperand(0), I->getOperand(1), "optimute"); 262 | case Instruction::FDiv: 263 | return BinaryOperator::Create(Instruction::FDiv, I->getOperand(0), I->getOperand(1), "optimute"); 264 | case Instruction::FRem: 265 | return BinaryOperator::Create(Instruction::FRem, I->getOperand(0), I->getOperand(1), "optimute"); 266 | 267 | case Instruction::And: 268 | return BinaryOperator::Create(Instruction::And, I->getOperand(0), I->getOperand(1), "optimute"); 269 | case Instruction::Or: 270 | return BinaryOperator::Create(Instruction::Or, I->getOperand(0), I->getOperand(1), "optimute"); 271 | case Instruction::Xor: 272 | return BinaryOperator::Create(Instruction::Xor, I->getOperand(0), I->getOperand(1), "optimute"); 273 | } 274 | } 275 | 276 | 277 | Instruction* getRequestedMutantIcmpInst(Instruction* I, ICmpInst* cmpInst) { 278 | switch(IcmpPred) { 279 | case CmpInst::ICMP_EQ: 280 | return CmpInst::Create(cmpInst->getOpcode(), CmpInst::ICMP_EQ, I->getOperand(0), I->getOperand(1), "optimute"); 281 | case CmpInst::ICMP_NE: 282 | return CmpInst::Create(cmpInst->getOpcode(), CmpInst::ICMP_NE, I->getOperand(0), I->getOperand(1), "optimute"); 283 | case CmpInst::ICMP_UGT: 284 | return CmpInst::Create(cmpInst->getOpcode(), CmpInst::ICMP_UGT, I->getOperand(0), I->getOperand(1), "optimute"); 285 | case CmpInst::ICMP_UGE: 286 | return CmpInst::Create(cmpInst->getOpcode(), CmpInst::ICMP_UGE, I->getOperand(0), I->getOperand(1), "optimute"); 287 | case CmpInst::ICMP_ULT: 288 | return CmpInst::Create(cmpInst->getOpcode(), CmpInst::ICMP_ULT, I->getOperand(0), I->getOperand(1), "optimute"); 289 | case CmpInst::ICMP_ULE: 290 | return CmpInst::Create(cmpInst->getOpcode(), CmpInst::ICMP_ULE, I->getOperand(0), I->getOperand(1), "optimute"); 291 | case CmpInst::ICMP_SGT: 292 | return CmpInst::Create(cmpInst->getOpcode(), CmpInst::ICMP_SGT, I->getOperand(0), I->getOperand(1), "optimute"); 293 | case CmpInst::ICMP_SGE: 294 | return CmpInst::Create(cmpInst->getOpcode(), CmpInst::ICMP_SGE, I->getOperand(0), I->getOperand(1), "optimute"); 295 | case CmpInst::ICMP_SLT: 296 | return CmpInst::Create(cmpInst->getOpcode(), CmpInst::ICMP_SLT, I->getOperand(0), I->getOperand(1), "optimute"); 297 | case CmpInst::ICMP_SLE: 298 | return CmpInst::Create(cmpInst->getOpcode(), CmpInst::ICMP_SLE, I->getOperand(0), I->getOperand(1), "optimute"); 299 | } 300 | } 301 | 302 | 303 | virtual bool runOnBasicBlock(BasicBlock &BB) { 304 | // figure out what instruction type are we dealing with 305 | // if icmp, figure out what predicate do we need 306 | for (BasicBlock::iterator DI = BB.begin(); DI != BB.end();) { 307 | Instruction *I = &*DI++; 308 | InstCount++; 309 | if (ICmpInst *cmpInst = dyn_cast(I)) { 310 | if (InstCount == MutationLocation) { 311 | Instruction *altI = getRequestedMutantIcmpInst(I, cmpInst); 312 | ReplaceInstWithInst(I, altI); 313 | } 314 | } 315 | else if (isa(*I)) { 316 | if (InstCount == MutationLocation) { 317 | Instruction* altI = getRequestedMutationBinaryOp(I); 318 | ReplaceInstWithInst(I, altI); 319 | } 320 | } 321 | } 322 | return false; 323 | } 324 | 325 | }; 326 | } 327 | 328 | char SwapBinaryOperators::ID = 0; 329 | static RegisterPass C("swapBinaryOperators", "Replace the binary operator."); 330 | 331 | 332 | namespace { 333 | struct DeleteInstr : public BasicBlockPass { 334 | static char ID; // Pass identification, replacement for typeid 335 | DeleteInstr() : BasicBlockPass(ID) {} 336 | 337 | virtual bool runOnBasicBlock(BasicBlock &BB) { 338 | for (BasicBlock::iterator DI = BB.begin(); DI != BB.end();) { 339 | Instruction *I = &*DI++; 340 | InstCount++; 341 | if (InstCount == DeleteLocation) { 342 | I->eraseFromParent(); 343 | } 344 | } 345 | return false; 346 | } 347 | 348 | }; 349 | } 350 | char DeleteInstr::ID = 0; 351 | static RegisterPass D("deleteInstr", "Delete an Instruction Mutation"); 352 | 353 | ////////////////////////////////////////////////////////////// 354 | /// CODE BELOW IS NOT REVIEWED/CLEANED, WE MAY NOT NEED IT /// 355 | ////////////////////////////////////////////////////////////// 356 | 357 | // InstCount, "Counts number of instructions"; 358 | // ConstantCount, "Counts number of constants"; 359 | // IcmpCount, "Counts number of Icmp instructions"; 360 | // LogicalCount, "Counts number of logical instructions"; 361 | // ArithCount, "Counts number of arithmetic instructions"; 362 | 363 | namespace { 364 | struct GetStats : public BasicBlockPass { 365 | static char ID; 366 | GetStats() : BasicBlockPass(ID) {} 367 | 368 | bool checkOpcode(unsigned opcode, int operators[], int len, int op_type) { 369 | for (int i = 0; i < len; i++) { 370 | if (operators[i] == opcode) 371 | return true; 372 | } 373 | return false; 374 | } 375 | 376 | void findConstantOperands(Instruction *I){ 377 | // loop through operands, 378 | int counter = -1; 379 | for (User::op_iterator i = I->op_begin(), e = I->op_end(); i != e; ++i) { 380 | counter++; 381 | Value *v = *i; 382 | if (isa(v)) { 383 | if (dyn_cast(v)) 384 | ConstantCount++; 385 | } 386 | } 387 | } 388 | 389 | virtual bool runOnBasicBlock(BasicBlock &BB) { 390 | int arithmeticOps[] = {Instruction::Add, Instruction::Sub, Instruction::Mul, Instruction::UDiv, Instruction::SDiv}; 391 | int floatingOps[] = {Instruction::FAdd, Instruction::FMul, Instruction::FDiv}; 392 | int logicalOps[] = {Instruction::And, Instruction::Or, Instruction::Xor}; 393 | 394 | for(BasicBlock::iterator DI = BB.begin(); DI != BB.end(); ) { 395 | Instruction *I = &*DI++; 396 | InstCount++; 397 | 398 | findConstantOperands(I); 399 | if (dyn_cast(I)) { 400 | IcmpCount++; 401 | } else { 402 | unsigned opcode = I->getOpcode(); 403 | bool done = checkOpcode(opcode, arithmeticOps, sizeof(arithmeticOps)/4, e_arith); 404 | if (done) 405 | ArithCount++; 406 | else { 407 | done = checkOpcode(opcode, logicalOps, sizeof(logicalOps)/4, e_logical); 408 | if (done) 409 | LogicalCount++; 410 | else { 411 | done = checkOpcode(opcode, floatingOps, sizeof(floatingOps)/4, e_arith); 412 | if (done) 413 | ArithCount++; 414 | } 415 | } 416 | } 417 | // // print the summary of findings so far 418 | // errs() << "Inst Summary: " << InstCount << "," << IcmpCount << "," << ConstantCount << "," << ArithCount << "," << LogicalCount << "\n"; 419 | } 420 | // print the summary of findings so far 421 | errs() << InstCount << "," << IcmpCount << "," << ConstantCount << "," << ArithCount << "," << LogicalCount << "\n"; 422 | } 423 | 424 | }; 425 | } 426 | char GetStats::ID = 0; 427 | static RegisterPass G("getStats", "Get some stats about mutation locs"); 428 | 429 | 430 | namespace { 431 | static const int num_opcodes = 59; 432 | static int opcodeCount[num_opcodes]; 433 | static int constCount[num_opcodes]; 434 | 435 | struct AllInstStats : public BasicBlockPass { 436 | static char ID; 437 | AllInstStats() : BasicBlockPass(ID) {} 438 | 439 | void printOpcodeStats() { 440 | for (int i = 0; i < num_opcodes; i++) { 441 | if (i==0) { 442 | errs() << "Total:" << constCount[i] << ":" << opcodeCount[i] << ","; 443 | } 444 | else { 445 | errs() << Instruction::getOpcodeName(i) << ":" << constCount[i] << ":" << opcodeCount[i] << ","; 446 | } 447 | if (i == num_opcodes -1) { 448 | errs() << "\n"; 449 | } 450 | } 451 | } 452 | 453 | void findConstantOperands(Instruction *I){ 454 | // loop through operands, 455 | int counter = -1; 456 | for (User::op_iterator i = I->op_begin(), e = I->op_end(); i != e; ++i) { 457 | counter++; 458 | Value *v = *i; 459 | if (isa(v)) { 460 | if (dyn_cast(v)) 461 | constCount[I->getOpcode()]++; 462 | } 463 | } 464 | } 465 | 466 | 467 | virtual bool runOnBasicBlock(BasicBlock &BB) { 468 | for (BasicBlock::iterator DI = BB.begin(); DI != BB.end(); ) { 469 | Instruction *I = &*DI++; 470 | opcodeCount[0]++; 471 | errs() << "the opcode is " << I->getOpcode() << "\n"; 472 | errs() << "LLVM thinks the opcode is: " << I->getOpcodeName() << "\n"; 473 | opcodeCount[I->getOpcode()]++; 474 | findConstantOperands(I); 475 | } 476 | printOpcodeStats(); 477 | return false; 478 | } 479 | 480 | }; 481 | } 482 | char AllInstStats::ID = 0; 483 | static RegisterPass A("allInstStats", "Get stats about all instructions"); 484 | -------------------------------------------------------------------------------- /IRMutation/README: -------------------------------------------------------------------------------- 1 | Instructions for compiling the LLVM passes: 2 | 3 | - copy the directory called Mutation (under ./LLVMPasses) as is into the 4 | LLVM source code tree under lib/Transforms/. 5 | (if you used build-llvm.sh to install LLVM locally, 6 | the directory will be ../llvm/lib/Transforms/) 7 | - add our new mutation pass (Mutation) into the make files under lib/Transforms. 8 | (either copy the provided Makefile and CMakeLists.txt from this directory 9 | to lib/Transforms 10 | if using LLVM 3.8.1 or adapt the existing ones if using another version) 11 | - go to your LLVM build directory and rebuild. You should have the mutation 12 | library generated under $build_directory/Release+Asserts/lib/LLVMMutate.so 13 | 14 | Instructions for building the LLVM instrumentation library: 15 | 16 | - navigate into the InstrumentationLib/ directory 17 | - run the script ./build-lib.sh 18 | - check that the library SRCIRORCoverageLib.o file exists now 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | University of Illinois/NCSA 2 | Open Source License 3 | 4 | Copyright (c) 2018 University of Illinois at Urbana-Champaign. 5 | All rights reserved. 6 | 7 | Developed by: Darko Marinov Group 8 | University of Illinois at Urbana-Champaign 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to 12 | deal with the Software without restriction, including without limitation the 13 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 14 | sell copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 1. Redistributions of source code must retain the above copyright notice, 17 | this list of conditions and the following disclaimers. 18 | 2. Redistributions in binary form must reproduce the above copyright 19 | notice, this list of conditions and the following disclaimers in the 20 | documentation and/or other materials provided with the distribution. 21 | 3. Neither the names of NCSA, University of Illinois, nor the names of its 22 | contributors may be used to endorse or promote products derived from this 23 | Software without specific prior written permission. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 30 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 31 | WITH THE SOFTWARE. 32 | 33 | Parts of this code has been adapted from the following two repositories: 34 | https://github.com/eliben 35 | https://github.com/eschulte/clang-mutate -------------------------------------------------------------------------------- /PythonWrappers/bashUtil.py: -------------------------------------------------------------------------------- 1 | import os, sys, subprocess, shutil, time, copy, fnmatch 2 | from collections import defaultdict 3 | import copy 4 | 5 | CWD = os.path.dirname(os.path.abspath(__file__)) 6 | 7 | def executeCommand(args, debug=False): 8 | """ Executes a command and times it. 9 | 10 | Args: 11 | args: a list of strings that constitute the bash command. 12 | debug: boolean flag, if true, prints the output to the commandline 13 | 14 | Returns: 15 | out: the output of running the command 16 | error: the error resulting from the command, if any. 17 | exec_time: the time spent to execute the command. 18 | """ 19 | start_time = time.time() 20 | p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 21 | out, err = p.communicate() 22 | end_time = time.time() 23 | out = decode(out) 24 | error = decode(err) 25 | if debug == True: 26 | print (str(args)) 27 | print (out) 28 | print (error) 29 | exec_time = str(end_time - start_time) 30 | return out, error, exec_time 31 | 32 | 33 | def decode(string): 34 | return string.decode('utf8', 'ignore') 35 | -------------------------------------------------------------------------------- /PythonWrappers/irCoverageClang: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os, sys 4 | import zlib 5 | 6 | CWD = os.path.dirname(os.path.abspath(__file__)) 7 | from bashUtil import executeCommand 8 | from irUtil import run 9 | 10 | 11 | def instrumentForCoverage(bitcode_file, src_file, opt_command, log_file): 12 | hash_code = zlib.adler32(bitcode_file + "+" + src_file) & 0xffffffff # Anding with 0xffffffff for Python compatibility issues 13 | command = opt_command + " -coverage " + bitcode_file + " -file_code=" + str(hash_code) + " -o " + bitcode_file 14 | with open(log_file, 'a') as log_file: 15 | log_file.write(bitcode_file + "+" + src_file + ":" + str(hash_code) + "\n") 16 | print("the current directory is: " + os.getcwd()) 17 | print("instrumenting with the command: " + command) 18 | executeCommand(command.split()) 19 | 20 | 21 | def main(): 22 | return run('coverage', instrumentForCoverage) 23 | 24 | 25 | if __name__ == '__main__': 26 | main() 27 | -------------------------------------------------------------------------------- /PythonWrappers/irMutationClang: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os, sys, subprocess, shutil, time, copy, fnmatch 4 | from collections import defaultdict 5 | import copy 6 | import zlib 7 | 8 | CWD = os.path.dirname(os.path.abspath(__file__)) 9 | from bashUtil import executeCommand 10 | from irUtil import getSummaryDir, run 11 | 12 | 13 | def collectMutations(bitcode_file, src_file, opt_command, dummy): # Using a dummy variable holder for last parameter for consistency 14 | hash_code = zlib.adler32(bitcode_file + "+" + src_file) & 0xffffffff 15 | command = opt_command + " -getBinaryOperators " + bitcode_file 16 | print("the current directory is: " + os.getcwd()) 17 | print("collecting binops mutants with the command: " + command) 18 | out1, err1, my_time1 = executeCommand(command.split()) 19 | 20 | command = opt_command + " -getICRMutationLocs " + bitcode_file 21 | print("the current directory is: " + os.getcwd()) 22 | print("collecting binops mutants with the command: " + command) 23 | out2, err2, my_time2 = executeCommand(command.split()) 24 | 25 | mutantsDir = os.path.join(getSummaryDir(), "bc-mutants") 26 | if not os.path.exists(mutantsDir): 27 | os.makedirs(mutantsDir) 28 | with open(os.path.join(mutantsDir, str(hash_code)), 'w') as log_file: 29 | for line in err1.split("\n"): 30 | if line: 31 | log_file.write("binaryOp:" + line + "\n") 32 | for line in err2.split("\n"): 33 | if line: 34 | log_file.write("const:" + line + "\n") 35 | 36 | 37 | def main(): 38 | return run('mutation', collectMutations) 39 | 40 | if __name__ == '__main__': 41 | main() 42 | -------------------------------------------------------------------------------- /PythonWrappers/irUtil.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | 3 | CWD = os.path.dirname(os.path.abspath(__file__)) 4 | from bashUtil import executeCommand 5 | 6 | def getSummaryDir(): 7 | # make the summary directory if does not exist 8 | # also makes the ir-coverage/ directory 9 | summaryDir = os.path.join(os.getenv("HOME"), ".srciror") 10 | if not os.path.exists(summaryDir): 11 | os.makedirs(summaryDir) 12 | return summaryDir 13 | 14 | 15 | def findSrcIndices(arg_list, ext): 16 | source_indices = [i for i, word in enumerate(arg_list) if word.endswith(ext)] 17 | return source_indices 18 | 19 | 20 | def run(task, taskFunction): 21 | llvm_bin_dir = os.environ["SRCIROR_LLVM_BIN"] 22 | clang = os.path.join(llvm_bin_dir, 'clang') 23 | opt = os.path.join(llvm_bin_dir, 'opt') 24 | mutation_lib = os.environ["SRCIROR_LLVMMutate_LIB"] # path to llvm-build/Release+Asserts/lib/LLVMMutate.so 25 | if task == "coverage": 26 | our_lib = os.environ["SRCIROR_COVINSTRUMENTATION_LIB"] # path to IRMutation/InstrumentationLib/SRCIRORCoverageLib.o 27 | else: 28 | our_lib = "" 29 | coverageDir = os.path.join(getSummaryDir(), "ir-coverage") # guarantees directory is made 30 | if not os.path.exists(coverageDir): 31 | os.makedirs(coverageDir) 32 | log_file = os.path.join(coverageDir, "hash-map") 33 | opt_command = opt + " -load " + mutation_lib 34 | args = sys.argv[1:] 35 | 36 | if '-fstack-usage' in args: # TODO: What is -fstack-usage? 37 | args.remove('-fstack-usage') 38 | compiler = clang 39 | print('logging compile flags: ' + ' '.join(args)) 40 | 41 | # if the build system is checking for the version flags, we don't mess it up, just delegate to the compiler 42 | if "--version" in args or "-V" in args: 43 | out, err, _ = executeCommand([compiler] + args, True) 44 | return 1 45 | 46 | ###### instrument ###### 47 | # if the command contains a flag that prevents linking, then it's 48 | # not generating an executable, then we should be able to generate 49 | # bitcode and instrument it for the task 50 | # 1. find if command is compiling some source .c file 51 | src_indices = findSrcIndices(args, ".c") 52 | if not src_indices: # there is no source code to instrument, so we want to try and link, no changes 53 | print('Looking to link: ' + str([compiler, our_lib] + args)) 54 | out, err, _ = executeCommand([compiler, our_lib] + args, True) 55 | print(str(out)) 56 | print(str(err)) 57 | return 1 # TODO: Why do we return 1 instead of 0? 58 | 59 | # if there are multiple src files, only care about the first 60 | src_index = src_indices[0] 61 | src_file = args[src_indices[0]] 62 | 63 | # 2. see if there is a specified -o; if exists, use it to determine name of intermediate .ll file 64 | new_args = list(args) 65 | try: 66 | dash_o_index = new_args.index('-o') 67 | out_name = new_args[dash_o_index + 1] 68 | print("we got the dash o index") 69 | print("the out name is : " + out_name) 70 | except: 71 | out_name = src_file # if does not exist, use the src file name as the base 72 | print("we did not find dash o index, so we are using src: " + src_file) 73 | dash_o_index = len(new_args) # filling up the args with some empty placeholders for upcoming update 74 | new_args += ["-o", ""] 75 | bitcode_file = os.path.dirname(out_name) + os.path.basename(out_name).split(".")[0] + ".ll" # expect only one . in the name 76 | print("the bitcode file name is: " + bitcode_file) 77 | 78 | # 3. put flags to generate bitcode 79 | new_args[dash_o_index + 1] = bitcode_file 80 | print("we are emitting llvm bitcode: " + " ".join([clang, '-S', '-emit-llvm'] + new_args)) 81 | executeCommand([clang, '-S', '-emit-llvm'] + new_args) 82 | 83 | # 4. instrument the bitcode for the specified task 84 | taskFunction(bitcode_file, src_file, opt_command, log_file) 85 | 86 | # 5. compile the .ll into the real output so compilation can proceed peacefully 87 | # replace the input C src file with the bitcode file we determined 88 | compiling_args = list(args) 89 | compiling_args[src_index] = bitcode_file 90 | command = [clang] + compiling_args + [our_lib] 91 | print("we are generating the output from the .ll with the command " + " ".join(command)) 92 | out, err, _ = executeCommand(command, True) 93 | print(str(out)) 94 | print(str(err)) 95 | return 1 96 | 97 | 98 | -------------------------------------------------------------------------------- /PythonWrappers/irVanillaClang: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os, sys 4 | 5 | CWD = os.path.dirname(os.path.abspath(__file__)) 6 | from bashUtil import executeCommand 7 | from irUtil import getSummaryDir, run 8 | 9 | 10 | def performOptimization(command, out_file): 11 | print("executing the opt command: " + command) 12 | out, err, my_time = executeCommand(command.split()) 13 | if err: 14 | # remove the generated file 15 | try: 16 | os.remove(out_file) 17 | except OSError: 18 | pass 19 | 20 | # log the failure 21 | print('Opt Failed To Mutate:' + out_file + '\n') 22 | print('ERROR IS:' + err) 23 | return -1 24 | return 1 25 | 26 | 27 | def opt(optimization, args, bitcode_file, opt_command): 28 | if optimization == 'replace_const': 29 | inst_count = args[0] 30 | op_count = args[1] 31 | val = args[2] 32 | out_file = bitcode_file 33 | command = opt_command + " -icrmutate -mutation_loc=" + inst_count + " -mutation_op_loc=" + op_count + " -mutation_val=" + val + " " + bitcode_file + " -o " + out_file 34 | elif optimization == 'replace_binary_op': 35 | inst_count = args[0] 36 | op_type = args[1] 37 | val = args[2] 38 | out_file = bitcode_file 39 | command = opt_command + " -swapBinaryOperators -mutation_loc=" + inst_count + " -icmp_pred=" + val + " -mutation_op=" + val + " " + bitcode_file + " -o " + out_file 40 | 41 | return performOptimization(command, out_file) 42 | 43 | 44 | def getMutationRequest(): 45 | path = os.path.join(getSummaryDir(), "mutation_request.txt") 46 | print("trying to find the file: " + path) 47 | if os.path.isfile(path): 48 | print("yes we found the file") 49 | with open(path, 'r') as mut_file: 50 | mutation = mut_file.readline().strip() 51 | return mutation 52 | else: 53 | return "" 54 | 55 | 56 | def mutateIfRequested(bitcode_file, src_file, opt_command, dummy): # Using a dummy variable holder for last parameter for consistency 57 | mutation = getMutationRequest() 58 | print("requested mutation is: " + mutation) 59 | if not mutation: 60 | return 61 | mutation_parts = mutation.split(":") 62 | files = mutation_parts[0].split("+") 63 | print("requested bitcode file: " + files[0] + " and src file is: " + files[1]) 64 | if bitcode_file != files[0] or src_file != files[1]: 65 | print("oh no, no mutation requested for bitcode: " + bitcode_file + " and the src file: " + src_file + ", reading " + files[0] + " and " + files[1]) 66 | return 67 | print("MUTATING...") 68 | mutation_type = mutation_parts[1] 69 | actual_mutation = ":".join(mutation_parts[2:]) 70 | info = actual_mutation.split(':') 71 | if mutation_type == "binaryOp": 72 | print("the info for binaryOp is: " + str(info)) 73 | inst_count = info[0] 74 | op_type = info[1] 75 | value = info[2] 76 | if opt('replace_binary_op', [inst_count, op_type, value], bitcode_file, opt_command) == -1: 77 | print("OPTIMUTE: MUTATION FAILED TO APPLY") 78 | else: # it's a replace constant 79 | print("the info for const is: " + str(info)) 80 | inst_count = info[0] 81 | op_count = info[1] 82 | value = info[3] 83 | if opt('replace_const', [inst_count, op_count, value], bitcode_file, opt_command) == -1: 84 | print("OPTIMUTE: MUTATION FAILED TO APPLY") 85 | 86 | 87 | def main(): 88 | run("mutation", mutateIfRequested) 89 | 90 | if __name__ == '__main__': 91 | main() 92 | -------------------------------------------------------------------------------- /PythonWrappers/mutationClang: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os, sys, subprocess, shutil, time, copy, fnmatch 4 | from collections import defaultdict 5 | import copy 6 | 7 | CWD = os.path.dirname(os.path.abspath(__file__)) 8 | from bashUtil import executeCommand 9 | 10 | 11 | def mutate(compiler, ext, mutator, args, compiler_include_flags, src_indices): 12 | new_args = copy.deepcopy(args) 13 | for index in src_indices: 14 | src_file = args[index] 15 | cov_info = getCoverageInfo(src_file) 16 | 17 | if not cov_info: 18 | print("no coverage info for the file: " + src_file + ", so assume wants to mutate all") 19 | cov_info = "all" 20 | command = mutator + " " + src_file + " -coverage_info=" + cov_info + " -- " + " ".join(args) + ' -I' + compiler_include_flags.strip() 21 | print("THE ACTUAL COMMAND IS: " + command) 22 | out, err, my_time = executeCommand(command.split(), True) 23 | 24 | 25 | def getCoverageInfo(src_file): 26 | print("THE CURRENT DIRECTORY IS: " + os.getcwd()) 27 | file_name = os.path.basename(src_file) + ".cov" 28 | cov_file = os.path.join(getSummaryDir(), "coverage") # Assume only one big coverage file, because only one tool at a time 29 | if not os.path.isfile(cov_file): 30 | return "" 31 | with open(cov_file, 'r') as in_file: 32 | lines = in_file.readlines() 33 | for line in lines: 34 | file_name = line.split(":")[0] 35 | if file_name != src_file: 36 | continue 37 | coverage = line.split(":")[1].strip(" ,\n") 38 | return coverage; 39 | 40 | 41 | def getSummaryDir(): 42 | # make the summary directory if does not exist 43 | # also makes the ir-coverage/ directory 44 | summaryDir = os.path.join(os.getenv("HOME"), ".srciror") 45 | if not os.path.exists(summaryDir): 46 | os.makedirs(summaryDir) 47 | return summaryDir 48 | 49 | 50 | def main(): 51 | llvm_bin_dir = os.environ["SRCIROR_LLVM_BIN"] 52 | compiler_include_flags = os.environ["SRCIROR_LLVM_INCLUDES"] # Release+Asserts/lib/clang/3.8.0/include 53 | clang = os.path.join(llvm_bin_dir, 'clang') 54 | mutator = os.environ["SRCIROR_SRC_MUTATOR"] # path to build/mutator 55 | args = sys.argv[1:] 56 | 57 | if '-fstack-usage' in args: # TODO: What is -fstack-usage? 58 | args.remove('-fstack-usage') 59 | compiler = clang 60 | print('logging compile flags: ' + ' '.join(args)) 61 | 62 | # if the build system is checking for the version flags, we don't mess it up, just delegate to the compiler 63 | if "--version" in args or "-V" in args: 64 | out, err, my_time = executeCommand([compiler] + args, True) 65 | return 1 66 | 67 | # mutate 68 | c_indices = [ i for i, word in enumerate(args) if word.endswith('.c')] # Mutating only .c files 69 | if c_indices: 70 | ext = ".c" 71 | mutate(compiler, ext, mutator, args, compiler_include_flags, c_indices) 72 | 73 | print('Looking to compile the original: ' + str([compiler] + args)) 74 | out, err, my_time = executeCommand([compiler] + args, True) 75 | return 1 76 | 77 | if __name__ == '__main__': 78 | main() 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # srciror 2 | SRCIROR (pronounced sorcerer) is a SRC and IR mutatOR. It performs mutations on source code written in C/C++ and on the LLVM IR. 3 | 4 | BUILDING: 5 | 6 | 0. Need dependencies: 7 | python 8 | python3 9 | clang 10 | lib32z1-dev 11 | 12 | 1. Build LLVM locally (if you do not already have it built). You can execute the provided script: 13 | `./llvm-build.sh` 14 | This creates llvm/ and llvm-build/ directories. 15 | 16 | 2. Build the SRC mutator. To build SRC mutator, see SRCMutation/README. 17 | 18 | 3. Build the IR mutator. To build IR mutator, see IRMutation/README. 19 | 20 | 4. Build the IR instrumentation code: 21 | `cd IRMutation/InstrumentationLib/; ./build-lib.sh` 22 | 23 | 5. Set up the environment variables needed by SRCIROR. 24 | 25 | `export SRCIROR_LLVMMutate_LIB=${PATH_TO_LLVM_BUILD}/Release+Asserts/lib/LLVMMutate.so` 26 | 27 | `export SRCIROR_SRC_MUTATOR=${PATH_TO_SRCIROR_DIR}/SRCMutation/build/mutator` 28 | 29 | `export SRCIROR_LLVM_BIN=${PATH_TO_LLVM_BUILD}/Release+Asserts/bin/` 30 | 31 | `export SRCIROR_LLVM_INCLUDES=${PATH_TO_LLVM_BUILD}/Release+Asserts/lib/clang/3.8.0/include/` 32 | 33 | `export SRCIROR_COVINSTRUMENTATION_LIB=${PATH_TO_SRCIROR_DIR}/InstrumentationLib/SRCIRORCoverageLib.o` 34 | 35 | Either set environment variables `PATH_TO_LLVM_BUILD` and `PATH_TO_SRCIROR_DIR`, 36 | or replace them with the corresponding values in the statements above. 37 | 38 | -------------------------------------------------------------------------------- /SRCMutation/Makefile: -------------------------------------------------------------------------------- 1 | CURR_DIR := $(shell pwd) 2 | LLVM_SRC_PATH := $(CURR_DIR)/../llvm 3 | LLVM_BUILD_PATH := $(CURR_DIR)/../llvm-build 4 | LLVM_BIN_PATH := $(LLVM_BUILD_PATH)/Release+Asserts/bin 5 | 6 | CXX := g++ 7 | CXXFLAGS := -fno-rtti -O0 -g 8 | PLUGIN_CXXFLAGS := -fpic 9 | 10 | LLVM_CXXFLAGS := `$(LLVM_BIN_PATH)/llvm-config --cxxflags` 11 | LLVM_LDFLAGS := `$(LLVM_BIN_PATH)/llvm-config --ldflags --libs --system-libs` 12 | LLVM_LDFLAGS_NOLIBS := `$(LLVM_BIN_PATH)/llvm-config --ldflags` 13 | PLUGIN_LDFLAGS := -shared 14 | 15 | # These are required when compiling vs. a source distribution of Clang. For 16 | # binary distributions llvm-config --cxxflags gives the right path. 17 | CLANG_INCLUDES := \ 18 | -I$(LLVM_SRC_PATH)/include \ 19 | -I$(LLVM_BUILD_PATH)/include \ 20 | -I$(LLVM_SRC_PATH)/tools/clang/include \ 21 | -I$(LLVM_BUILD_PATH)/tools/clang/include 22 | 23 | # List of Clang libraries to link. The proper -L will be provided by the 24 | # call to llvm-config 25 | CLANG_LIBS := \ 26 | -Wl,--start-group \ 27 | -lclangAST \ 28 | -lclangAnalysis \ 29 | -lclangBasic \ 30 | -lclangDriver \ 31 | -lclangEdit \ 32 | -lclangFrontend \ 33 | -lclangFrontendTool \ 34 | -lclangLex \ 35 | -lclangParse \ 36 | -lclangSema \ 37 | -lclangEdit \ 38 | -lclangASTMatchers \ 39 | -lclangRewrite \ 40 | -lclangRewriteFrontend \ 41 | -lclangStaticAnalyzerFrontend \ 42 | -lclangStaticAnalyzerCheckers \ 43 | -lclangStaticAnalyzerCore \ 44 | -lclangSerialization \ 45 | -lclangToolingCore \ 46 | -lclangTooling \ 47 | -Wl,--end-group 48 | 49 | # Internal paths in this project: where to find sources, and where to put 50 | # build artifacts. 51 | SRC_CLANG_DIR := src 52 | BUILDDIR := build 53 | 54 | .PHONY: all 55 | all: make_builddir \ 56 | emit_build_config \ 57 | $(BUILDDIR)/mutator \ 58 | 59 | 60 | .PHONY: emit_build_config 61 | emit_build_config: make_builddir 62 | @echo $(LLVM_BIN_PATH) > $(BUILDDIR)/_build_config 63 | 64 | .PHONY: make_builddir 65 | make_builddir: 66 | @test -d $(BUILDDIR) || mkdir $(BUILDDIR) 67 | 68 | $(BUILDDIR)/mutator: $(SRC_CLANG_DIR)/mutator.cpp 69 | $(CXX) $(CXXFLAGS) $(LLVM_CXXFLAGS) $(CLANG_INCLUDES) $^ \ 70 | $(CLANG_LIBS) $(LLVM_LDFLAGS) -o $@ 71 | 72 | .PHONY: clean 73 | clean: 74 | rm -rf $(BUILDDIR)/* *.dot test/*.pyc test/__pycache__ 75 | -------------------------------------------------------------------------------- /SRCMutation/README: -------------------------------------------------------------------------------- 1 | To build the mutator, one needs to have a source of LLVM and a build of it locally. 2 | The top of the Makefile assumes that these exist under ../llvm and ../llvm-build. 3 | We have included a script for convenience to download the LLVM sources for version 3.8.1 and 4 | build it. 5 | 6 | ** If one wants to use an LLVM in a different location, change the paths to LLVM at the beginning 7 | of the Makefile 8 | -------------------------------------------------------------------------------- /SRCMutation/src/mutator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "clang/ASTMatchers/ASTMatchers.h" 10 | #include "clang/ASTMatchers/ASTMatchFinder.h" 11 | #include "clang/Basic/SourceManager.h" 12 | #include "clang/Basic/TargetInfo.h" 13 | #include "clang/Basic/TargetOptions.h" 14 | #include "clang/Frontend/CompilerInstance.h" 15 | #include "clang/Frontend/FrontendActions.h" 16 | #include "clang/Frontend/TextDiagnosticPrinter.h" 17 | #include "clang/Lex/Lexer.h" 18 | #include "clang/Rewrite/Core/Rewriter.h" 19 | #include "clang/Rewrite/Frontend/Rewriters.h" 20 | #include "clang/Tooling/CommonOptionsParser.h" 21 | #include "clang/Tooling/Refactoring.h" 22 | #include "clang/Tooling/Tooling.h" 23 | #include "clang/Tooling/Core/Replacement.h" 24 | #include "llvm/Support/raw_ostream.h" 25 | 26 | using namespace clang; 27 | using namespace clang::ast_matchers; 28 | using namespace clang::driver; 29 | using namespace clang::tooling; 30 | 31 | static llvm::cl::OptionCategory OptimuteMutatorCategory("Optimute Mutator"); 32 | static llvm::cl::opt CoverageInfo("coverage_info", llvm::cl::desc("Specify the lines covered by the test suite and should mutate"), llvm::cl::value_desc("string")); 33 | static std::set CovSet; 34 | static bool MutateAll = false; 35 | 36 | class IfCondHandler : public MatchFinder::MatchCallback { 37 | 38 | public: 39 | IfCondHandler(Replacements* Replace, std::string Binder, CompilerInstance* TheCompInst) : Replace(Replace), CI(TheCompInst) { 40 | this->Binder = Binder; 41 | } 42 | 43 | virtual void run(const MatchFinder::MatchResult &Result) { 44 | if (const IfStmt *CondStmt = Result.Nodes.getNodeAs(Binder)) { 45 | int lineNumber = Result.SourceManager->getSpellingLineNumber(CondStmt->getIfLoc()); 46 | if (!MutateAll && CovSet.find(lineNumber) == CovSet.end()) { // Line is not in the set, and it's not MutateAll 47 | // printf("The match we found: %i is not in the set of covered lines\n", lineNumber); 48 | return; 49 | } 50 | // printf("Found the line %i and it is covered\n", lineNumber); 51 | const Expr *Condition = CondStmt->getCond(); 52 | bool invalid; 53 | 54 | CharSourceRange conditionRange = CharSourceRange::getTokenRange(Condition->getLocStart(), Condition->getLocEnd()); 55 | StringRef str = Lexer::getSourceText(conditionRange, *(Result.SourceManager), CI->getLangOpts(), &invalid); 56 | std::string MutatedString = "!(" + str.str() + ")"; 57 | Replacement Rep(*(Result.SourceManager), Condition->getLocStart(), str.str().size(), MutatedString); 58 | Replace->insert(Rep); 59 | } 60 | } 61 | 62 | private: 63 | Replacements *Replace; 64 | CompilerInstance *CI; 65 | std::string Binder; 66 | }; 67 | 68 | class WhileCondHandler : public MatchFinder::MatchCallback { 69 | 70 | public: 71 | WhileCondHandler(Replacements* Replace, std::string Binder, CompilerInstance* TheCompInst) : Replace(Replace), CI(TheCompInst) { 72 | this->Binder = Binder; 73 | } 74 | 75 | virtual void run(const MatchFinder::MatchResult &Result) { 76 | if (const WhileStmt *CondStmt = Result.Nodes.getNodeAs(Binder)) { 77 | int lineNumber = Result.SourceManager->getSpellingLineNumber(CondStmt->getWhileLoc()); 78 | if (!MutateAll && CovSet.find(lineNumber) == CovSet.end()) { // Line is not in the set, and it's not MutateAll 79 | // printf("The match we found: %i is not in the set of covered lines\n", lineNumber); 80 | return; 81 | } 82 | // printf("Found the line %i and it is covered\n", lineNumber); 83 | const Expr *Condition = CondStmt->getCond(); 84 | bool invalid; 85 | 86 | CharSourceRange conditionRange = CharSourceRange::getTokenRange(Condition->getLocStart(), Condition->getLocEnd()); // Getting char size of condition 87 | StringRef str = Lexer::getSourceText(conditionRange, *(Result.SourceManager), CI->getLangOpts(), &invalid); 88 | std::string MutatedString = "!(" + str.str() + ")"; 89 | Replacement Rep(*(Result.SourceManager), Condition->getLocStart(), str.str().size(), MutatedString); 90 | Replace->insert(Rep); 91 | } 92 | } 93 | 94 | private: 95 | Replacements *Replace; 96 | CompilerInstance *CI; 97 | std::string Binder; 98 | }; 99 | 100 | class ConstHandler : public MatchFinder::MatchCallback { 101 | 102 | public: 103 | ConstHandler(Replacements* Replace, std::string Binder) : Replace(Replace) { 104 | this->Binder = Binder; 105 | } 106 | 107 | virtual void run(const MatchFinder::MatchResult &Result) { 108 | 109 | if (const IntegerLiteral *IntLiteral = Result.Nodes.getNodeAs(Binder)) { 110 | int lineNumber = Result.SourceManager->getSpellingLineNumber(IntLiteral->getLocation()); 111 | if (!MutateAll && CovSet.find(lineNumber) == CovSet.end()) { // Line is not in the set, and it's not MutateAll 112 | // printf("The match we found: %i is not in the set of covered lines\n", lineNumber); 113 | return; 114 | } 115 | // printf("Found the line %i and it is covered\n", lineNumber); 116 | 117 | std::string ValueStr = IntLiteral->getValue().toString(10, true); 118 | char* endptr; 119 | long long Value = std::strtoll(ValueStr.c_str(), &endptr, 10); 120 | int Size = ValueStr.size(); 121 | std::vector Values; 122 | if (Value == 1) { 123 | Values.insert(Values.end(), {"(-1)", "0", "2"}); 124 | } 125 | else if (Value == -1) { 126 | Values.insert(Values.end(), {"1", "0", "(-2)"}); 127 | } 128 | else if (Value == 0) { 129 | Values.insert(Values.end(), {"1", "(-1)"}); 130 | } 131 | else { 132 | Values.insert(Values.end(), {"0", "1", "(-1)", "-(" + std::to_string(Value) + ")", std::to_string(Value + 1), std::to_string(Value - 1)}); 133 | } 134 | for (std::string MutationVal : Values) { 135 | // printf("subsituting for the value: %s\n", MutationVal.c_str()); 136 | Replacement Rep(*(Result.SourceManager), IntLiteral->getLocStart(), Size, MutationVal); 137 | Replace->insert(Rep); 138 | } 139 | } 140 | } 141 | 142 | private: 143 | Replacements *Replace; 144 | std::string Binder; 145 | }; 146 | 147 | 148 | 149 | 150 | class BinaryOpHandler : public MatchFinder::MatchCallback { 151 | 152 | public: 153 | BinaryOpHandler(Replacements* Replace, std::string OpName, std::string Op, std::string OpType) : Replace(Replace) { 154 | this->OpName = OpName; 155 | this->Op = Op; 156 | this->OpType = OpType; 157 | if (OpType == "arith") { 158 | CurrOps = &ArithmeticOperators; 159 | } 160 | else if (OpType == "rel") { 161 | CurrOps = &RelationalOperators; 162 | } 163 | else if (OpType == "logical") { 164 | CurrOps = &LogicalOperators; 165 | } 166 | else if (OpType == "bitwise") { 167 | CurrOps = &BitwiseOperators; 168 | } 169 | else if (OpType == "arithAssign") { 170 | CurrOps = &ArithAssignOperators; 171 | } 172 | else if (OpType == "bitwiseAssign") { 173 | CurrOps = &BitwiseAssignOperators; 174 | } 175 | } 176 | 177 | virtual void run(const MatchFinder::MatchResult &Result) { 178 | 179 | if (const BinaryOperator *BinOp = Result.Nodes.getNodeAs(OpName)) { 180 | int lineNumber = Result.SourceManager->getSpellingLineNumber(BinOp->getOperatorLoc()); 181 | if (!MutateAll && CovSet.find(lineNumber) == CovSet.end()) { // Line is not in the set, and it's not MutateAll 182 | // printf("The match we found: %i is not in the set of covered lines\n", lineNumber); 183 | return; 184 | } 185 | // printf("Found the line %i and it is covered\n", lineNumber); 186 | 187 | int Size = Op.size(); 188 | for (std::string MutationOp : *CurrOps) { 189 | if (MutationOp == Op) { 190 | continue; 191 | } 192 | Replacement Rep(*(Result.SourceManager), BinOp->getOperatorLoc(), Size, MutationOp); 193 | Replace->insert(Rep); 194 | } 195 | } 196 | } 197 | 198 | private: 199 | Replacements *Replace; 200 | std::string OpName; 201 | std::string Op; 202 | std::string OpType; 203 | std::vector* CurrOps; 204 | std::vector ArithmeticOperators = {"+", "-", "*", "/", "%"}; 205 | std::vector RelationalOperators = {"<", "<=", ">", ">=", "==", "!="}; 206 | std::vector LogicalOperators = {"&&", "||"}; 207 | std::vector BitwiseOperators = {"&", "|", "^"}; 208 | std::vector ArithAssignOperators = {"+=", "-=", "*=", "/=", "%="}; 209 | std::vector BitwiseAssignOperators = {"&=", "|=", "^="}; 210 | 211 | }; 212 | 213 | 214 | bool applyReplacement(const Replacement &Replace, Rewriter &Rewrite) { 215 | bool Result = true; 216 | if (Replace.isApplicable()) { 217 | Result = Replace.apply(Rewrite); 218 | } 219 | else { 220 | Result = false; 221 | } 222 | 223 | return Result; 224 | } 225 | 226 | 227 | void Mutate(Replacements& repl, std::string NamePrefix, std::string NameSuffix, std::string tool, std::string ext, std::string srcDir, SourceManager& SourceMgr, CompilerInstance& TheCompInst, FileManager& FileMgr){ 228 | int x = 0; 229 | for (auto &r : repl) { 230 | x++; 231 | std::string pName = r.getFilePath().str(); 232 | std::string fName = tool + ext; 233 | if (pName.length() >= fName.length()) { 234 | if (pName.compare(pName.length() - fName.length(), fName.length(), fName) == 0) { 235 | Rewriter TheRewriter; 236 | TheRewriter.setSourceMgr(SourceMgr, TheCompInst.getLangOpts()); 237 | 238 | // Set the main file handled by the source manager to the input file. 239 | const FileEntry *FileIn = FileMgr.getFile(srcDir + "/" + fName); 240 | SourceMgr.setMainFileID( 241 | SourceMgr.createFileID(FileIn, SourceLocation(), SrcMgr::C_User)); 242 | TheCompInst.getDiagnosticClient().BeginSourceFile( 243 | TheCompInst.getLangOpts(), &TheCompInst.getPreprocessor()); 244 | 245 | applyReplacement(r, TheRewriter); 246 | const RewriteBuffer *RewriteBuf = TheRewriter.getRewriteBufferFor(SourceMgr.getMainFileID()); 247 | std::ofstream out_file; 248 | std::string outFileName = srcDir + "/" + tool + ".mut." + NamePrefix + std::to_string(x) + "." + std::to_string(r.getOffset()) + "." + NameSuffix + ext; 249 | if (access(outFileName.c_str(), F_OK) == -1) { 250 | out_file.open(outFileName); 251 | out_file << std::string(RewriteBuf->begin(), RewriteBuf->end()); 252 | out_file.close(); 253 | SourceLocation startLoc = SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID()); 254 | SourceLocation mutloc = startLoc.getLocWithOffset(r.getOffset()); 255 | int lineNumber = SourceMgr.getSpellingLineNumber(mutloc); 256 | printf("line: %i %s\n", lineNumber, (tool + ".mut." + NamePrefix + std::to_string(x) + "." + std::to_string(r.getOffset()) + "." + NameSuffix + ext).c_str()); // Outputting where mutants are 257 | } 258 | else { 259 | printf("ERROR IN GENERATING MUTANTS: we have a name overlap for %s\n", outFileName.c_str()); 260 | } 261 | } 262 | } 263 | } 264 | } 265 | 266 | 267 | std::set parseCoverageLines(std::string ListOfLines) { 268 | std::stringstream ss(ListOfLines); 269 | std::set result; 270 | 271 | while (ss.good()) { 272 | std::string substr; 273 | getline(ss, substr, ','); 274 | // If the value is "all", then means want to mutate all, so MutateAll=true 275 | if (!substr.compare("all")) { 276 | MutateAll = true; 277 | return result; 278 | } 279 | result.insert(std::stoi(substr)); 280 | } 281 | 282 | return result; 283 | } 284 | 285 | 286 | int main(int argc, const char **argv) { 287 | CommonOptionsParser op(argc, argv, OptimuteMutatorCategory); 288 | RefactoringTool AORTool(op.getCompilations(), op.getSourcePathList()); 289 | RefactoringTool RORTool(op.getCompilations(), op.getSourcePathList()); 290 | RefactoringTool ICRTool(op.getCompilations(), op.getSourcePathList()); 291 | RefactoringTool LCRTool(op.getCompilations(), op.getSourcePathList()); 292 | RefactoringTool OCNGTool(op.getCompilations(), op.getSourcePathList()); 293 | 294 | 295 | CompilerInstance TheCompInst; 296 | TheCompInst.createDiagnostics(); 297 | 298 | LangOptions &lo = TheCompInst.getLangOpts(); 299 | lo.CPlusPlus = 1; // Works on C++ code 300 | 301 | // Initialize target info with the default triple for our platform. 302 | auto TO = std::make_shared(); 303 | TO->Triple = llvm::sys::getDefaultTargetTriple(); 304 | TargetInfo *TI = TargetInfo::CreateTargetInfo(TheCompInst.getDiagnostics(), TO); 305 | TheCompInst.setTarget(TI); 306 | 307 | TheCompInst.createFileManager(); 308 | FileManager &FileMgr = TheCompInst.getFileManager(); 309 | TheCompInst.createSourceManager(FileMgr); 310 | SourceManager &SourceMgr = TheCompInst.getSourceManager(); 311 | TheCompInst.createPreprocessor(TU_Module); 312 | TheCompInst.createASTContext(); 313 | 314 | 315 | 316 | // Set up AST matcher callbacks. 317 | BinaryOpHandler HandlerForAddOp(&AORTool.getReplacements(), "addOp", "+", "arith"); 318 | BinaryOpHandler HandlerForSubOp(&AORTool.getReplacements(), "subOp", "-", "arith"); 319 | BinaryOpHandler HandlerForMulOp(&AORTool.getReplacements(), "mulOp", "*", "arith"); 320 | BinaryOpHandler HandlerForDivOp(&AORTool.getReplacements(), "divOp", "/", "arith"); 321 | BinaryOpHandler HandlerForModuloOp(&AORTool.getReplacements(), "moduloOp", "%", "arith"); 322 | 323 | BinaryOpHandler HandlerForLtOp(&RORTool.getReplacements(), "ltOp", "<", "rel"); 324 | BinaryOpHandler HandlerForLeOp(&RORTool.getReplacements(), "leOp", "<=", "rel"); 325 | BinaryOpHandler HandlerForGtOp(&RORTool.getReplacements(), "gtOp", ">", "rel"); 326 | BinaryOpHandler HandlerForGeOp(&RORTool.getReplacements(), "geOp", ">=", "rel"); 327 | BinaryOpHandler HandlerForEqOp(&RORTool.getReplacements(), "eqOp", "==", "rel"); 328 | BinaryOpHandler HandlerForNeqOp(&RORTool.getReplacements(), "neqOp", "!=", "rel"); 329 | 330 | BinaryOpHandler HandlerForAndOp(&LCRTool.getReplacements(), "logicAndOp", "&&", "logical"); 331 | BinaryOpHandler HandlerForOrOp(&LCRTool.getReplacements(), "logicOrOp", "||", "logical"); 332 | 333 | BinaryOpHandler HandlerForBitAndOp(&LCRTool.getReplacements(), "bitwiseAndOp", "&", "bitwise"); 334 | BinaryOpHandler HandlerForBitOrOp(&LCRTool.getReplacements(), "bitwiseOrOp", "|", "bitwise"); 335 | BinaryOpHandler HandlerForBitXorOp(&LCRTool.getReplacements(), "bitwiseXorOp", "^", "bitwise"); 336 | 337 | BinaryOpHandler HandlerForAddAssignOp(&AORTool.getReplacements(), "addAssignOp", "+=", "arithAssign"); 338 | BinaryOpHandler HandlerForSubAssignOp(&AORTool.getReplacements(), "subAssignOp", "-=", "arithAssign"); 339 | BinaryOpHandler HandlerForMulAssignOp(&AORTool.getReplacements(), "mulAssignOp", "*=", "arithAssign"); 340 | BinaryOpHandler HandlerForDivAssignOp(&AORTool.getReplacements(), "divAssignOp", "/=", "arithAssign"); 341 | BinaryOpHandler HandlerForModuloAssignOp(&AORTool.getReplacements(), "moduloAssignOp", "%=", "arithAssign"); 342 | 343 | 344 | BinaryOpHandler HandlerForAndAssignOp(&LCRTool.getReplacements(), "andAssignOp", "&=", "bitwiseAssign"); 345 | BinaryOpHandler HandlerForOrAssignOp(&LCRTool.getReplacements(), "orAssignOp", "|=", "bitwiseAssign"); 346 | BinaryOpHandler HandlerForXorAssignOp(&LCRTool.getReplacements(), "xorAssignOp", "^=", "bitwiseAssign"); 347 | 348 | ConstHandler HandlerForConst(&ICRTool.getReplacements(), "intConst"); 349 | IfCondHandler HandlerForIf(&OCNGTool.getReplacements(), "if", &TheCompInst); 350 | WhileCondHandler HandlerForWhile(&OCNGTool.getReplacements(), "while", &TheCompInst); 351 | 352 | MatchFinder AORFinder; 353 | AORFinder.addMatcher(binaryOperator(hasOperatorName("+")).bind("addOp"), &HandlerForAddOp); 354 | AORFinder.addMatcher(binaryOperator(hasOperatorName("-")).bind("subOp"), &HandlerForSubOp); 355 | AORFinder.addMatcher(binaryOperator(hasOperatorName("*")).bind("mulOp"), &HandlerForMulOp); 356 | AORFinder.addMatcher(binaryOperator(hasOperatorName("/")).bind("divOp"), &HandlerForDivOp); 357 | AORFinder.addMatcher(binaryOperator(hasOperatorName("%")).bind("moduloOp"), &HandlerForModuloOp); 358 | 359 | AORFinder.addMatcher(binaryOperator(hasOperatorName("+=")).bind("addAssignOp"), &HandlerForAddAssignOp); 360 | AORFinder.addMatcher(binaryOperator(hasOperatorName("-=")).bind("subAssignOp"), &HandlerForSubAssignOp); 361 | AORFinder.addMatcher(binaryOperator(hasOperatorName("*=")).bind("mulAssignOp"), &HandlerForMulAssignOp); 362 | AORFinder.addMatcher(binaryOperator(hasOperatorName("/=")).bind("divAssignOp"), &HandlerForDivAssignOp); 363 | AORFinder.addMatcher(binaryOperator(hasOperatorName("%=")).bind("moduloAssignOp"), &HandlerForModuloAssignOp); 364 | 365 | 366 | MatchFinder RORFinder; 367 | RORFinder.addMatcher(binaryOperator(hasOperatorName("<")).bind("ltOp"), &HandlerForLtOp); 368 | RORFinder.addMatcher(binaryOperator(hasOperatorName("<=")).bind("leOp"), &HandlerForLeOp); 369 | RORFinder.addMatcher(binaryOperator(hasOperatorName(">")).bind("gtOp"), &HandlerForGtOp); 370 | RORFinder.addMatcher(binaryOperator(hasOperatorName(">=")).bind("geOp"), &HandlerForGeOp); 371 | RORFinder.addMatcher(binaryOperator(hasOperatorName("==")).bind("eqOp"), &HandlerForEqOp); 372 | RORFinder.addMatcher(binaryOperator(hasOperatorName("!=")).bind("neqOp"), &HandlerForNeqOp); 373 | 374 | MatchFinder LCRFinder; 375 | LCRFinder.addMatcher(binaryOperator(hasOperatorName("&&")).bind("logicAndOp"), &HandlerForAndOp); 376 | LCRFinder.addMatcher(binaryOperator(hasOperatorName("||")).bind("logicOrOp"), &HandlerForOrOp); 377 | 378 | LCRFinder.addMatcher(binaryOperator(hasOperatorName("&")).bind("bitwiseAndOp"), &HandlerForBitAndOp); 379 | LCRFinder.addMatcher(binaryOperator(hasOperatorName("|")).bind("bitwiseOrOp"), &HandlerForBitOrOp); 380 | LCRFinder.addMatcher(binaryOperator(hasOperatorName("^")).bind("bitwiseXorOp"), &HandlerForBitXorOp); 381 | 382 | 383 | LCRFinder.addMatcher(binaryOperator(hasOperatorName("&=")).bind("andAssignOp"), &HandlerForAndAssignOp); 384 | LCRFinder.addMatcher(binaryOperator(hasOperatorName("|=")).bind("orAssignOp"), &HandlerForOrAssignOp); 385 | LCRFinder.addMatcher(binaryOperator(hasOperatorName("^=")).bind("xorAssignOp"), &HandlerForXorAssignOp); 386 | 387 | MatchFinder ICRFinder; 388 | ICRFinder.addMatcher(integerLiteral().bind("intConst"), &HandlerForConst); 389 | 390 | MatchFinder OCNGFinder; 391 | OCNGFinder.addMatcher(ifStmt().bind("if"), &HandlerForIf); 392 | OCNGFinder.addMatcher(whileStmt().bind("while"), &HandlerForWhile); 393 | 394 | std::string FileName = argv[1]; // Assumes only one source file on command line to mutate 395 | CovSet = parseCoverageLines(CoverageInfo); 396 | std::string ToolDotExt = FileName.substr(FileName.find_last_of("/\\") + 1); 397 | std::string::size_type const p(ToolDotExt.find_last_of('.')); 398 | std::string CurrTool = ToolDotExt.substr(0, p); 399 | std::string Ext = ToolDotExt.substr(p, ToolDotExt.length()); 400 | std::string SrcDir = FileName.substr(0, FileName.find_last_of("/\\")); 401 | 402 | int failed = 0; 403 | if (int Result = AORTool.run(newFrontendActionFactory(&AORFinder).get())) { 404 | failed = 1; 405 | } 406 | else { 407 | Mutate(AORTool.getReplacements(), "binaryop_for_", "60", CurrTool, Ext, SrcDir, SourceMgr, TheCompInst, FileMgr); 408 | } 409 | //----- 410 | if (int Result = RORTool.run(newFrontendActionFactory(&RORFinder).get())) { 411 | failed = 1; 412 | } 413 | else { 414 | Mutate(RORTool.getReplacements(), "binaryop_for_", "61", CurrTool, Ext, SrcDir, SourceMgr, TheCompInst, FileMgr); 415 | } 416 | //----- 417 | if (int Result = ICRTool.run(newFrontendActionFactory(&ICRFinder).get())) { 418 | failed = 1; 419 | } 420 | else { 421 | Mutate(ICRTool.getReplacements(), "const_for_", "const", CurrTool, Ext, SrcDir, SourceMgr, TheCompInst, FileMgr); 422 | } 423 | //----- 424 | if (int Result = LCRTool.run(newFrontendActionFactory(&LCRFinder).get())) { 425 | failed = 1; 426 | } 427 | else { 428 | Mutate(LCRTool.getReplacements(), "binaryop_for_", "62", CurrTool, Ext, SrcDir, SourceMgr, TheCompInst, FileMgr); 429 | } 430 | //----- 431 | if (int Result = OCNGTool.run(newFrontendActionFactory(&OCNGFinder).get())) { 432 | failed = 1; 433 | } 434 | else { 435 | Mutate(OCNGTool.getReplacements(), "binaryop_for_", "ocng", CurrTool, Ext, SrcDir, SourceMgr, TheCompInst, FileMgr); 436 | } 437 | 438 | return failed; 439 | } 440 | -------------------------------------------------------------------------------- /llvm-build.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash -e 2 | 3 | # ================= Main Body ===================== # 4 | ZIPS_DIR=zips 5 | TOOLS_DIR=tools 6 | LLVM_DIR=llvm 7 | LLVM_BUILD=llvm-build 8 | mkdir -p $ZIPS_DIR 9 | mkdir -p $TOOLS_DIR 10 | # get the sources of clang, compiler-rt, and llvm 11 | wget -N http://llvm.org/releases/3.8.1/llvm-3.8.1.src.tar.xz -P $ZIPS_DIR/ 12 | wget -N http://llvm.org/releases/3.8.1/compiler-rt-3.8.1.src.tar.xz -P $ZIPS_DIR/ 13 | wget -N http://llvm.org/releases/3.8.1/cfe-3.8.1.src.tar.xz -P $ZIPS_DIR/ 14 | 15 | # unzip 16 | tar xfJ $ZIPS_DIR/llvm-3.8.1.src.tar.xz 17 | tar xfJ $ZIPS_DIR/compiler-rt-3.8.1.src.tar.xz 18 | tar xfJ $ZIPS_DIR/cfe-3.8.1.src.tar.xz 19 | 20 | # relocate the sources 21 | rm -rf $LLVM_DIR 22 | mv llvm-3.8.1.src $LLVM_DIR 23 | mv compiler-rt-3.8.1.src $LLVM_DIR/projects/compiler-rt 24 | mv cfe-3.8.1.src $LLVM_DIR/tools/clang 25 | 26 | # build llvm 27 | rm -rf $LLVM_BUILD 28 | mkdir $LLVM_BUILD 29 | cd $LLVM_BUILD 30 | export C_INCLUDE_PATH= 31 | export CPLUS_INCLUDE_PATH= 32 | ../llvm/configure 33 | # cmake -G "Unix Makefiles" ../llvm 34 | make -j4 35 | --------------------------------------------------------------------------------