2019-02-28 21:13:38 +01:00
|
|
|
//===- InstrOrderFile.cpp ---- Late IR instrumentation for order file ----===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
Sink all InitializePasses.h includes
This file lists every pass in LLVM, and is included by Pass.h, which is
very popular. Every time we add, remove, or rename a pass in LLVM, it
caused lots of recompilation.
I found this fact by looking at this table, which is sorted by the
number of times a file was changed over the last 100,000 git commits
multiplied by the number of object files that depend on it in the
current checkout:
recompiles touches affected_files header
342380 95 3604 llvm/include/llvm/ADT/STLExtras.h
314730 234 1345 llvm/include/llvm/InitializePasses.h
307036 118 2602 llvm/include/llvm/ADT/APInt.h
213049 59 3611 llvm/include/llvm/Support/MathExtras.h
170422 47 3626 llvm/include/llvm/Support/Compiler.h
162225 45 3605 llvm/include/llvm/ADT/Optional.h
158319 63 2513 llvm/include/llvm/ADT/Triple.h
140322 39 3598 llvm/include/llvm/ADT/StringRef.h
137647 59 2333 llvm/include/llvm/Support/Error.h
131619 73 1803 llvm/include/llvm/Support/FileSystem.h
Before this change, touching InitializePasses.h would cause 1345 files
to recompile. After this change, touching it only causes 550 compiles in
an incremental rebuild.
Reviewers: bkramer, asbirlea, bollu, jdoerfert
Differential Revision: https://reviews.llvm.org/D70211
2019-11-13 22:15:01 +01:00
|
|
|
#include "llvm/Transforms/Instrumentation/InstrOrderFile.h"
|
2019-02-28 21:13:38 +01:00
|
|
|
#include "llvm/ADT/Statistic.h"
|
|
|
|
#include "llvm/IR/Constants.h"
|
|
|
|
#include "llvm/IR/Function.h"
|
|
|
|
#include "llvm/IR/GlobalValue.h"
|
|
|
|
#include "llvm/IR/IRBuilder.h"
|
|
|
|
#include "llvm/IR/Instruction.h"
|
|
|
|
#include "llvm/IR/Instructions.h"
|
|
|
|
#include "llvm/IR/Metadata.h"
|
|
|
|
#include "llvm/IR/Module.h"
|
Sink all InitializePasses.h includes
This file lists every pass in LLVM, and is included by Pass.h, which is
very popular. Every time we add, remove, or rename a pass in LLVM, it
caused lots of recompilation.
I found this fact by looking at this table, which is sorted by the
number of times a file was changed over the last 100,000 git commits
multiplied by the number of object files that depend on it in the
current checkout:
recompiles touches affected_files header
342380 95 3604 llvm/include/llvm/ADT/STLExtras.h
314730 234 1345 llvm/include/llvm/InitializePasses.h
307036 118 2602 llvm/include/llvm/ADT/APInt.h
213049 59 3611 llvm/include/llvm/Support/MathExtras.h
170422 47 3626 llvm/include/llvm/Support/Compiler.h
162225 45 3605 llvm/include/llvm/ADT/Optional.h
158319 63 2513 llvm/include/llvm/ADT/Triple.h
140322 39 3598 llvm/include/llvm/ADT/StringRef.h
137647 59 2333 llvm/include/llvm/Support/Error.h
131619 73 1803 llvm/include/llvm/Support/FileSystem.h
Before this change, touching InitializePasses.h would cause 1345 files
to recompile. After this change, touching it only causes 550 compiles in
an incremental rebuild.
Reviewers: bkramer, asbirlea, bollu, jdoerfert
Differential Revision: https://reviews.llvm.org/D70211
2019-11-13 22:15:01 +01:00
|
|
|
#include "llvm/InitializePasses.h"
|
2019-02-28 21:13:38 +01:00
|
|
|
#include "llvm/Pass.h"
|
|
|
|
#include "llvm/PassRegistry.h"
|
|
|
|
#include "llvm/ProfileData/InstrProf.h"
|
|
|
|
#include "llvm/Support/CommandLine.h"
|
|
|
|
#include "llvm/Support/Debug.h"
|
|
|
|
#include "llvm/Support/FileSystem.h"
|
|
|
|
#include "llvm/Support/Path.h"
|
|
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
#include "llvm/Transforms/Instrumentation.h"
|
|
|
|
#include <fstream>
|
|
|
|
#include <map>
|
2019-03-01 16:25:24 +01:00
|
|
|
#include <mutex>
|
2019-02-28 21:13:38 +01:00
|
|
|
#include <set>
|
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
using namespace llvm;
|
|
|
|
#define DEBUG_TYPE "instrorderfile"
|
|
|
|
|
|
|
|
static cl::opt<std::string> ClOrderFileWriteMapping(
|
|
|
|
"orderfile-write-mapping", cl::init(""),
|
|
|
|
cl::desc(
|
|
|
|
"Dump functions and their MD5 hash to deobfuscate profile data"),
|
|
|
|
cl::Hidden);
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
// We need a global bitmap to tell if a function is executed. We also
|
|
|
|
// need a global variable to save the order of functions. We can use a
|
|
|
|
// fixed-size buffer that saves the MD5 hash of the function. We need
|
|
|
|
// a global variable to save the index into the buffer.
|
|
|
|
|
|
|
|
std::mutex MappingMutex;
|
|
|
|
|
|
|
|
struct InstrOrderFile {
|
|
|
|
private:
|
|
|
|
GlobalVariable *OrderFileBuffer;
|
|
|
|
GlobalVariable *BufferIdx;
|
|
|
|
GlobalVariable *BitMap;
|
|
|
|
ArrayType *BufferTy;
|
|
|
|
ArrayType *MapTy;
|
|
|
|
|
|
|
|
public:
|
|
|
|
InstrOrderFile() {}
|
|
|
|
|
|
|
|
void createOrderFileData(Module &M) {
|
|
|
|
LLVMContext &Ctx = M.getContext();
|
|
|
|
int NumFunctions = 0;
|
|
|
|
for (Function &F : M) {
|
|
|
|
if (!F.isDeclaration())
|
|
|
|
NumFunctions++;
|
|
|
|
}
|
|
|
|
|
|
|
|
BufferTy =
|
|
|
|
ArrayType::get(Type::getInt64Ty(Ctx), INSTR_ORDER_FILE_BUFFER_SIZE);
|
|
|
|
Type *IdxTy = Type::getInt32Ty(Ctx);
|
|
|
|
MapTy = ArrayType::get(Type::getInt8Ty(Ctx), NumFunctions);
|
|
|
|
|
|
|
|
// Create the global variables.
|
|
|
|
std::string SymbolName = INSTR_PROF_ORDERFILE_BUFFER_NAME_STR;
|
|
|
|
OrderFileBuffer = new GlobalVariable(M, BufferTy, false, GlobalValue::LinkOnceODRLinkage,
|
|
|
|
Constant::getNullValue(BufferTy), SymbolName);
|
|
|
|
Triple TT = Triple(M.getTargetTriple());
|
|
|
|
OrderFileBuffer->setSection(
|
|
|
|
getInstrProfSectionName(IPSK_orderfile, TT.getObjectFormat()));
|
|
|
|
|
|
|
|
std::string IndexName = INSTR_PROF_ORDERFILE_BUFFER_IDX_NAME_STR;
|
|
|
|
BufferIdx = new GlobalVariable(M, IdxTy, false, GlobalValue::LinkOnceODRLinkage,
|
|
|
|
Constant::getNullValue(IdxTy), IndexName);
|
|
|
|
|
|
|
|
std::string BitMapName = "bitmap_0";
|
|
|
|
BitMap = new GlobalVariable(M, MapTy, false, GlobalValue::PrivateLinkage,
|
|
|
|
Constant::getNullValue(MapTy), BitMapName);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate the code sequence in the entry block of each function to
|
|
|
|
// update the buffer.
|
|
|
|
void generateCodeSequence(Module &M, Function &F, int FuncId) {
|
|
|
|
if (!ClOrderFileWriteMapping.empty()) {
|
|
|
|
std::lock_guard<std::mutex> LogLock(MappingMutex);
|
|
|
|
std::error_code EC;
|
2019-08-05 07:43:48 +02:00
|
|
|
llvm::raw_fd_ostream OS(ClOrderFileWriteMapping, EC,
|
|
|
|
llvm::sys::fs::OF_Append);
|
2019-02-28 21:13:38 +01:00
|
|
|
if (EC) {
|
|
|
|
report_fatal_error(Twine("Failed to open ") + ClOrderFileWriteMapping +
|
|
|
|
" to save mapping file for order file instrumentation\n");
|
|
|
|
} else {
|
|
|
|
std::stringstream stream;
|
|
|
|
stream << std::hex << MD5Hash(F.getName());
|
|
|
|
std::string singleLine = "MD5 " + stream.str() + " " +
|
|
|
|
std::string(F.getName()) + '\n';
|
|
|
|
OS << singleLine;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BasicBlock *OrigEntry = &F.getEntryBlock();
|
|
|
|
|
|
|
|
LLVMContext &Ctx = M.getContext();
|
|
|
|
IntegerType *Int32Ty = Type::getInt32Ty(Ctx);
|
|
|
|
IntegerType *Int8Ty = Type::getInt8Ty(Ctx);
|
|
|
|
|
|
|
|
// Create a new entry block for instrumentation. We will check the bitmap
|
|
|
|
// in this basic block.
|
|
|
|
BasicBlock *NewEntry =
|
|
|
|
BasicBlock::Create(M.getContext(), "order_file_entry", &F, OrigEntry);
|
|
|
|
IRBuilder<> entryB(NewEntry);
|
|
|
|
// Create a basic block for updating the circular buffer.
|
|
|
|
BasicBlock *UpdateOrderFileBB =
|
|
|
|
BasicBlock::Create(M.getContext(), "order_file_set", &F, OrigEntry);
|
|
|
|
IRBuilder<> updateB(UpdateOrderFileBB);
|
|
|
|
|
|
|
|
// Check the bitmap, if it is already 1, do nothing.
|
|
|
|
// Otherwise, set the bit, grab the index, update the buffer.
|
|
|
|
Value *IdxFlags[] = {ConstantInt::get(Int32Ty, 0),
|
|
|
|
ConstantInt::get(Int32Ty, FuncId)};
|
|
|
|
Value *MapAddr = entryB.CreateGEP(MapTy, BitMap, IdxFlags, "");
|
|
|
|
LoadInst *loadBitMap = entryB.CreateLoad(Int8Ty, MapAddr, "");
|
|
|
|
entryB.CreateStore(ConstantInt::get(Int8Ty, 1), MapAddr);
|
|
|
|
Value *IsNotExecuted =
|
|
|
|
entryB.CreateICmpEQ(loadBitMap, ConstantInt::get(Int8Ty, 0));
|
|
|
|
entryB.CreateCondBr(IsNotExecuted, UpdateOrderFileBB, OrigEntry);
|
|
|
|
|
|
|
|
// Fill up UpdateOrderFileBB: grab the index, update the buffer!
|
|
|
|
Value *IdxVal = updateB.CreateAtomicRMW(
|
|
|
|
AtomicRMWInst::Add, BufferIdx, ConstantInt::get(Int32Ty, 1),
|
2021-02-09 05:07:12 +01:00
|
|
|
MaybeAlign(), AtomicOrdering::SequentiallyConsistent);
|
2019-02-28 21:13:38 +01:00
|
|
|
// We need to wrap around the index to fit it inside the buffer.
|
|
|
|
Value *WrappedIdx = updateB.CreateAnd(
|
|
|
|
IdxVal, ConstantInt::get(Int32Ty, INSTR_ORDER_FILE_BUFFER_MASK));
|
|
|
|
Value *BufferGEPIdx[] = {ConstantInt::get(Int32Ty, 0), WrappedIdx};
|
|
|
|
Value *BufferAddr =
|
|
|
|
updateB.CreateGEP(BufferTy, OrderFileBuffer, BufferGEPIdx, "");
|
|
|
|
updateB.CreateStore(ConstantInt::get(Type::getInt64Ty(Ctx), MD5Hash(F.getName())),
|
|
|
|
BufferAddr);
|
|
|
|
updateB.CreateBr(OrigEntry);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool run(Module &M) {
|
|
|
|
createOrderFileData(M);
|
|
|
|
|
|
|
|
int FuncId = 0;
|
|
|
|
for (Function &F : M) {
|
|
|
|
if (F.isDeclaration())
|
|
|
|
continue;
|
|
|
|
generateCodeSequence(M, F, FuncId);
|
|
|
|
++FuncId;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
}; // End of InstrOrderFile struct
|
|
|
|
|
|
|
|
class InstrOrderFileLegacyPass : public ModulePass {
|
|
|
|
public:
|
|
|
|
static char ID;
|
|
|
|
|
|
|
|
InstrOrderFileLegacyPass() : ModulePass(ID) {
|
|
|
|
initializeInstrOrderFileLegacyPassPass(
|
|
|
|
*PassRegistry::getPassRegistry());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool runOnModule(Module &M) override;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // End anonymous namespace
|
|
|
|
|
|
|
|
bool InstrOrderFileLegacyPass::runOnModule(Module &M) {
|
|
|
|
if (skipModule(M))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return InstrOrderFile().run(M);
|
|
|
|
}
|
|
|
|
|
|
|
|
PreservedAnalyses
|
|
|
|
InstrOrderFilePass::run(Module &M, ModuleAnalysisManager &AM) {
|
|
|
|
if (InstrOrderFile().run(M))
|
|
|
|
return PreservedAnalyses::none();
|
|
|
|
return PreservedAnalyses::all();
|
|
|
|
}
|
|
|
|
|
|
|
|
INITIALIZE_PASS_BEGIN(InstrOrderFileLegacyPass, "instrorderfile",
|
|
|
|
"Instrumentation for Order File", false, false)
|
|
|
|
INITIALIZE_PASS_END(InstrOrderFileLegacyPass, "instrorderfile",
|
|
|
|
"Instrumentation for Order File", false, false)
|
|
|
|
|
|
|
|
char InstrOrderFileLegacyPass::ID = 0;
|
|
|
|
|
|
|
|
ModulePass *llvm::createInstrOrderFilePass() {
|
|
|
|
return new InstrOrderFileLegacyPass();
|
|
|
|
}
|