Index: lib/Transforms/Instrumentation/EfficiencySanitizer.cpp =================================================================== --- lib/Transforms/Instrumentation/EfficiencySanitizer.cpp +++ lib/Transforms/Instrumentation/EfficiencySanitizer.cpp @@ -30,6 +30,7 @@ #include "llvm/IR/Type.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/ModuleUtils.h" @@ -85,6 +86,8 @@ 6, // ESAN_WorkingSet: 64B:1B, so 64 to 1 == >>6. }; +static const unsigned MaxStructCounterNameSize = 512; + namespace { static EfficiencySanitizerOptions @@ -99,6 +102,11 @@ if (Options.ToolType == EfficiencySanitizerOptions::ESAN_None) Options.ToolType = EfficiencySanitizerOptions::ESAN_CacheFrag; + if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) { + // FIXME: re-enable load/store instrumentation for the shadow based + // CacheFrag tool. + ClInstrumentLoadsAndStores = false; + } return Options; } @@ -130,6 +138,9 @@ private: bool initOnModule(Module &M); void initializeCallbacks(Module &M); + bool shouldIgnoreStructType(StructType *StructTy); + StringRef createStructCounterName( + StructType *StructTy, SmallString &NameStr); void createCacheFragGV(Module &M, SmallVector &ToolInfoVec); GlobalVariable *createEsanInitToolGV(Module &M); void createDestructor(Module &M, GlobalVariable *GV); @@ -220,11 +231,130 @@ IRB.getInt32Ty(), IntptrTy, nullptr)); } +bool EfficiencySanitizer::shouldIgnoreStructType(StructType *StructTy) { + if (StructTy == nullptr || StructTy->isOpaque() || StructTy->isLiteral()) + return true; + // Ignore union or struct with only 1 field. + if (StructTy->getNumElements() <= 1) + return true; + return false; +} + +StringRef EfficiencySanitizer::createStructCounterName( + StructType *StructTy, SmallString &NameStr) { + // Append NumOfFields and field type ids to avoid struct conflicts + // with the same name but different fields. + if (StructTy->hasName()) + NameStr += StructTy->getName(); + else + NameStr += "struct.anon"; + // Append #NumOfFields + NameStr += "#"; + Twine(StructTy->getNumElements()).toVector(NameStr); + // Append struct field type ids in the reverse order. + for (int i = StructTy->getNumElements() - 1; i >= 0; --i) { + NameStr += "#"; + Twine(StructTy->getElementType(i)->getTypeID()).toVector(NameStr); + // MaxStructCounterNameSize is a soft size limit. + // We allow the actual size to be larger and append at least + // one field type id. + if (NameStr.size() >= MaxStructCounterNameSize) + break; + } + return StringRef(NameStr); +} + // Create the global variable for the cache-fragmentation tool. void EfficiencySanitizer::createCacheFragGV( Module &M, SmallVector &ToolInfoVec) { assert(Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag); - // FIXME: expand ToolInfoVec for the cache-fragmentation tool. + // This structure should be kept consistent with the tool-specific struct + // in the runtime library. + // struct ToolInfo { + // const char *UnitName; + // // Expanding for the cache-fragmentation tool. + // unsigned NumOfStructs; + // struct StructInfoTy { + // const char *StructName; + // unsigned NumOfFields; + // uint64_t *Counters; + // const char **TypeName; + // } *Structs; + // }; + ArrayType *ArrayTy; + IntegerType *Int32Ty = Type::getInt32Ty(*Ctx); + IntegerType *Int64Ty = Type::getInt64Ty(*Ctx); + PointerType *Int8PtrTy = Type::getInt8PtrTy(*Ctx); + PointerType *Int8PtrPtrTy = Int8PtrTy->getPointerTo(); + PointerType *Int64PtrTy = Type::getInt64PtrTy(*Ctx); + StructType *StructInfoTy = + StructType::get(Int8PtrTy, Int32Ty, Int64PtrTy, Int8PtrPtrTy, nullptr); + PointerType *StructInfoPtrTy = StructInfoTy->getPointerTo(); + + unsigned NumOfStructs = 0; + SmallVector Initializers; + std::vector Vec = M.getIdentifiedStructTypes(); + for (auto &StructTy : Vec) { + if (shouldIgnoreStructType(StructTy)) + continue; + ++NumOfStructs; + + // StructName. + SmallString NameStr; + StringRef NameRef = createStructCounterName(StructTy, NameStr); + GlobalVariable *StructCounterName = createPrivateGlobalForString( + M, NameRef, /*AllowMerging*/true); + + // Counters. + // We create the counter array with StructCounterName and weak linkage + // so that the structs with the same name and layout from different + // compilation units are merged into one. + ArrayTy = ArrayType::get(Int64Ty, StructTy->getNumElements()); + GlobalVariable *Counters = + new GlobalVariable(M, ArrayTy, false, + GlobalVariable::WeakAnyLinkage, + ConstantAggregateZero::get(ArrayTy), + NameRef); + + // Field type name array. + // We pass field type names to runtime for better user report. + ArrayTy = ArrayType::get(Int8PtrTy, StructTy->getNumElements()); + GlobalVariable *TypeName = + new GlobalVariable(M, ArrayTy, true, + GlobalVariable::InternalLinkage, nullptr); + SmallVector TypeNameVec; + for (unsigned i = 0; i < StructTy->getNumElements(); ++i) { + Type *Ty = StructTy->getElementType(i); + std::string Str; + raw_string_ostream StrOS(Str); + Ty->print(StrOS); + TypeNameVec.push_back( + ConstantExpr::getPointerCast( + createPrivateGlobalForString(M, StrOS.str(), true), + Int8PtrTy)); + } + TypeName->setInitializer(ConstantArray::get(ArrayTy, TypeNameVec)); + + Initializers.push_back(ConstantStruct::get( + StructInfoTy, + ConstantExpr::getPointerCast(StructCounterName, Int8PtrTy), + ConstantInt::get(Int32Ty, StructTy->getNumElements()), + ConstantExpr::getPointerCast(Counters, Int64PtrTy), + ConstantExpr::getPointerCast(TypeName, Int8PtrPtrTy), + nullptr)); + } + + // Structs: StructInfo array + ArrayTy = ArrayType::get(StructInfoTy, NumOfStructs); + GlobalVariable *StructInfo = new GlobalVariable( + M, ArrayTy, false ,GlobalVariable::InternalLinkage, + ConstantArray::get(ArrayTy, Initializers)); + + // NumOfStructs + ToolInfoVec.push_back(ConstantInt::get(Int32Ty, NumOfStructs)); + // Structs + ToolInfoVec.push_back(ConstantExpr::getPointerCast(StructInfo, + StructInfoPtrTy)); } // Create the tool-specific global variable passed to EsanInit and EsanExit.