xref: /freebsd-src/contrib/llvm-project/llvm/lib/Transforms/Instrumentation/InstrOrderFile.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
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