10b57cec5SDimitry Andric //===- InstrOrderFile.cpp ---- Late IR instrumentation for order file ----===//
20b57cec5SDimitry Andric //
3349cc55cSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4349cc55cSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5349cc55cSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
100b57cec5SDimitry Andric
11480093f4SDimitry Andric #include "llvm/Transforms/Instrumentation/InstrOrderFile.h"
120b57cec5SDimitry Andric #include "llvm/IR/Constants.h"
130b57cec5SDimitry Andric #include "llvm/IR/Function.h"
140b57cec5SDimitry Andric #include "llvm/IR/GlobalValue.h"
150b57cec5SDimitry Andric #include "llvm/IR/IRBuilder.h"
160b57cec5SDimitry Andric #include "llvm/IR/Instructions.h"
170b57cec5SDimitry Andric #include "llvm/IR/Module.h"
180b57cec5SDimitry Andric #include "llvm/ProfileData/InstrProf.h"
190b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h"
200b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h"
210b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
220b57cec5SDimitry Andric #include "llvm/Transforms/Instrumentation.h"
230b57cec5SDimitry Andric #include <fstream>
240b57cec5SDimitry Andric #include <mutex>
250b57cec5SDimitry Andric #include <sstream>
260b57cec5SDimitry Andric
270b57cec5SDimitry Andric using namespace llvm;
280b57cec5SDimitry Andric #define DEBUG_TYPE "instrorderfile"
290b57cec5SDimitry Andric
300b57cec5SDimitry Andric static cl::opt<std::string> ClOrderFileWriteMapping(
310b57cec5SDimitry Andric "orderfile-write-mapping", cl::init(""),
320b57cec5SDimitry Andric cl::desc(
330b57cec5SDimitry Andric "Dump functions and their MD5 hash to deobfuscate profile data"),
340b57cec5SDimitry Andric cl::Hidden);
350b57cec5SDimitry Andric
360b57cec5SDimitry Andric namespace {
370b57cec5SDimitry Andric
380b57cec5SDimitry Andric // We need a global bitmap to tell if a function is executed. We also
390b57cec5SDimitry Andric // need a global variable to save the order of functions. We can use a
400b57cec5SDimitry Andric // fixed-size buffer that saves the MD5 hash of the function. We need
410b57cec5SDimitry Andric // a global variable to save the index into the buffer.
420b57cec5SDimitry Andric
430b57cec5SDimitry Andric std::mutex MappingMutex;
440b57cec5SDimitry Andric
450b57cec5SDimitry Andric struct InstrOrderFile {
460b57cec5SDimitry Andric private:
470b57cec5SDimitry Andric GlobalVariable *OrderFileBuffer;
480b57cec5SDimitry Andric GlobalVariable *BufferIdx;
490b57cec5SDimitry Andric GlobalVariable *BitMap;
500b57cec5SDimitry Andric ArrayType *BufferTy;
510b57cec5SDimitry Andric ArrayType *MapTy;
520b57cec5SDimitry Andric
530b57cec5SDimitry Andric public:
54*81ad6265SDimitry Andric InstrOrderFile() = default;
550b57cec5SDimitry Andric
createOrderFileData__anonfa9c886f0111::InstrOrderFile560b57cec5SDimitry Andric void createOrderFileData(Module &M) {
570b57cec5SDimitry Andric LLVMContext &Ctx = M.getContext();
580b57cec5SDimitry Andric int NumFunctions = 0;
590b57cec5SDimitry Andric for (Function &F : M) {
600b57cec5SDimitry Andric if (!F.isDeclaration())
610b57cec5SDimitry Andric NumFunctions++;
620b57cec5SDimitry Andric }
630b57cec5SDimitry Andric
640b57cec5SDimitry Andric BufferTy =
650b57cec5SDimitry Andric ArrayType::get(Type::getInt64Ty(Ctx), INSTR_ORDER_FILE_BUFFER_SIZE);
660b57cec5SDimitry Andric Type *IdxTy = Type::getInt32Ty(Ctx);
670b57cec5SDimitry Andric MapTy = ArrayType::get(Type::getInt8Ty(Ctx), NumFunctions);
680b57cec5SDimitry Andric
690b57cec5SDimitry Andric // Create the global variables.
700b57cec5SDimitry Andric std::string SymbolName = INSTR_PROF_ORDERFILE_BUFFER_NAME_STR;
710b57cec5SDimitry Andric OrderFileBuffer = new GlobalVariable(M, BufferTy, false, GlobalValue::LinkOnceODRLinkage,
720b57cec5SDimitry Andric Constant::getNullValue(BufferTy), SymbolName);
730b57cec5SDimitry Andric Triple TT = Triple(M.getTargetTriple());
740b57cec5SDimitry Andric OrderFileBuffer->setSection(
750b57cec5SDimitry Andric getInstrProfSectionName(IPSK_orderfile, TT.getObjectFormat()));
760b57cec5SDimitry Andric
770b57cec5SDimitry Andric std::string IndexName = INSTR_PROF_ORDERFILE_BUFFER_IDX_NAME_STR;
780b57cec5SDimitry Andric BufferIdx = new GlobalVariable(M, IdxTy, false, GlobalValue::LinkOnceODRLinkage,
790b57cec5SDimitry Andric Constant::getNullValue(IdxTy), IndexName);
800b57cec5SDimitry Andric
810b57cec5SDimitry Andric std::string BitMapName = "bitmap_0";
820b57cec5SDimitry Andric BitMap = new GlobalVariable(M, MapTy, false, GlobalValue::PrivateLinkage,
830b57cec5SDimitry Andric Constant::getNullValue(MapTy), BitMapName);
840b57cec5SDimitry Andric }
850b57cec5SDimitry Andric
860b57cec5SDimitry Andric // Generate the code sequence in the entry block of each function to
870b57cec5SDimitry Andric // update the buffer.
generateCodeSequence__anonfa9c886f0111::InstrOrderFile880b57cec5SDimitry Andric void generateCodeSequence(Module &M, Function &F, int FuncId) {
890b57cec5SDimitry Andric if (!ClOrderFileWriteMapping.empty()) {
900b57cec5SDimitry Andric std::lock_guard<std::mutex> LogLock(MappingMutex);
910b57cec5SDimitry Andric std::error_code EC;
928bcb0991SDimitry Andric llvm::raw_fd_ostream OS(ClOrderFileWriteMapping, EC,
938bcb0991SDimitry Andric llvm::sys::fs::OF_Append);
940b57cec5SDimitry Andric if (EC) {
950b57cec5SDimitry Andric report_fatal_error(Twine("Failed to open ") + ClOrderFileWriteMapping +
960b57cec5SDimitry Andric " to save mapping file for order file instrumentation\n");
970b57cec5SDimitry Andric } else {
980b57cec5SDimitry Andric std::stringstream stream;
990b57cec5SDimitry Andric stream << std::hex << MD5Hash(F.getName());
1000b57cec5SDimitry Andric std::string singleLine = "MD5 " + stream.str() + " " +
1010b57cec5SDimitry Andric std::string(F.getName()) + '\n';
1020b57cec5SDimitry Andric OS << singleLine;
1030b57cec5SDimitry Andric }
1040b57cec5SDimitry Andric }
1050b57cec5SDimitry Andric
1060b57cec5SDimitry Andric BasicBlock *OrigEntry = &F.getEntryBlock();
1070b57cec5SDimitry Andric
1080b57cec5SDimitry Andric LLVMContext &Ctx = M.getContext();
1090b57cec5SDimitry Andric IntegerType *Int32Ty = Type::getInt32Ty(Ctx);
1100b57cec5SDimitry Andric IntegerType *Int8Ty = Type::getInt8Ty(Ctx);
1110b57cec5SDimitry Andric
1120b57cec5SDimitry Andric // Create a new entry block for instrumentation. We will check the bitmap
1130b57cec5SDimitry Andric // in this basic block.
1140b57cec5SDimitry Andric BasicBlock *NewEntry =
1150b57cec5SDimitry Andric BasicBlock::Create(M.getContext(), "order_file_entry", &F, OrigEntry);
1160b57cec5SDimitry Andric IRBuilder<> entryB(NewEntry);
1170b57cec5SDimitry Andric // Create a basic block for updating the circular buffer.
1180b57cec5SDimitry Andric BasicBlock *UpdateOrderFileBB =
1190b57cec5SDimitry Andric BasicBlock::Create(M.getContext(), "order_file_set", &F, OrigEntry);
1200b57cec5SDimitry Andric IRBuilder<> updateB(UpdateOrderFileBB);
1210b57cec5SDimitry Andric
1220b57cec5SDimitry Andric // Check the bitmap, if it is already 1, do nothing.
1230b57cec5SDimitry Andric // Otherwise, set the bit, grab the index, update the buffer.
1240b57cec5SDimitry Andric Value *IdxFlags[] = {ConstantInt::get(Int32Ty, 0),
1250b57cec5SDimitry Andric ConstantInt::get(Int32Ty, FuncId)};
1260b57cec5SDimitry Andric Value *MapAddr = entryB.CreateGEP(MapTy, BitMap, IdxFlags, "");
1270b57cec5SDimitry Andric LoadInst *loadBitMap = entryB.CreateLoad(Int8Ty, MapAddr, "");
1280b57cec5SDimitry Andric entryB.CreateStore(ConstantInt::get(Int8Ty, 1), MapAddr);
1290b57cec5SDimitry Andric Value *IsNotExecuted =
1300b57cec5SDimitry Andric entryB.CreateICmpEQ(loadBitMap, ConstantInt::get(Int8Ty, 0));
1310b57cec5SDimitry Andric entryB.CreateCondBr(IsNotExecuted, UpdateOrderFileBB, OrigEntry);
1320b57cec5SDimitry Andric
1330b57cec5SDimitry Andric // Fill up UpdateOrderFileBB: grab the index, update the buffer!
1340b57cec5SDimitry Andric Value *IdxVal = updateB.CreateAtomicRMW(
1350b57cec5SDimitry Andric AtomicRMWInst::Add, BufferIdx, ConstantInt::get(Int32Ty, 1),
136fe6060f1SDimitry Andric MaybeAlign(), AtomicOrdering::SequentiallyConsistent);
1370b57cec5SDimitry Andric // We need to wrap around the index to fit it inside the buffer.
1380b57cec5SDimitry Andric Value *WrappedIdx = updateB.CreateAnd(
1390b57cec5SDimitry Andric IdxVal, ConstantInt::get(Int32Ty, INSTR_ORDER_FILE_BUFFER_MASK));
1400b57cec5SDimitry Andric Value *BufferGEPIdx[] = {ConstantInt::get(Int32Ty, 0), WrappedIdx};
1410b57cec5SDimitry Andric Value *BufferAddr =
1420b57cec5SDimitry Andric updateB.CreateGEP(BufferTy, OrderFileBuffer, BufferGEPIdx, "");
1430b57cec5SDimitry Andric updateB.CreateStore(ConstantInt::get(Type::getInt64Ty(Ctx), MD5Hash(F.getName())),
1440b57cec5SDimitry Andric BufferAddr);
1450b57cec5SDimitry Andric updateB.CreateBr(OrigEntry);
1460b57cec5SDimitry Andric }
1470b57cec5SDimitry Andric
run__anonfa9c886f0111::InstrOrderFile1480b57cec5SDimitry Andric bool run(Module &M) {
1490b57cec5SDimitry Andric createOrderFileData(M);
1500b57cec5SDimitry Andric
1510b57cec5SDimitry Andric int FuncId = 0;
1520b57cec5SDimitry Andric for (Function &F : M) {
1530b57cec5SDimitry Andric if (F.isDeclaration())
1540b57cec5SDimitry Andric continue;
1550b57cec5SDimitry Andric generateCodeSequence(M, F, FuncId);
1560b57cec5SDimitry Andric ++FuncId;
1570b57cec5SDimitry Andric }
1580b57cec5SDimitry Andric
1590b57cec5SDimitry Andric return true;
1600b57cec5SDimitry Andric }
1610b57cec5SDimitry Andric
1620b57cec5SDimitry Andric }; // End of InstrOrderFile struct
1630b57cec5SDimitry Andric } // End anonymous namespace
1640b57cec5SDimitry Andric
1650b57cec5SDimitry Andric PreservedAnalyses
run(Module & M,ModuleAnalysisManager & AM)1660b57cec5SDimitry Andric InstrOrderFilePass::run(Module &M, ModuleAnalysisManager &AM) {
1670b57cec5SDimitry Andric if (InstrOrderFile().run(M))
1680b57cec5SDimitry Andric return PreservedAnalyses::none();
1690b57cec5SDimitry Andric return PreservedAnalyses::all();
1700b57cec5SDimitry Andric }
171