//===- LineProfiling.cpp - Insert counters for line profiling -------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This pass creates counters for the number of times that the original source // lines of code were executed. // // The lines are found from existing debug info in the LLVM IR. Iterating // through LLVM instructions, every time the debug location changes we insert a // new counter and instructions to increment the counter there. A global // destructor runs to dump the counters out to a file. // //===----------------------------------------------------------------------===// #define DEBUG_TYPE "insert-line-profiling" #include "ProfilingUtils.h" #include "llvm/Transforms/Instrumentation.h" #include "llvm/Analysis/DebugInfo.h" #include "llvm/Module.h" #include "llvm/Pass.h" #include "llvm/Instructions.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/Debug.h" #include "llvm/Support/DebugLoc.h" #include "llvm/Support/InstIterator.h" #include "llvm/Support/IRBuilder.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringExtras.h" #include #include using namespace llvm; STATISTIC(NumUpdatesInserted, "The # of counter increments inserted."); namespace { class LineProfiler : public ModulePass { bool runOnModule(Module &M); public: static char ID; LineProfiler() : ModulePass(ID) { initializeLineProfilerPass(*PassRegistry::getPassRegistry()); } virtual const char *getPassName() const { return "Line Profiler"; } private: // Get pointers to the functions in the runtime library. Constant *getStartFileFunc(); Constant *getCounterFunc(); Constant *getEndFileFunc(); // Insert an increment of the counter before instruction I. void InsertCounterUpdateBefore(Instruction *I); // Add the function to write out all our counters to the global destructor // list. void InsertCounterWriteout(); // Mapping from the source location to the counter tracking that location. DenseMap counters; Module *Mod; LLVMContext *Ctx; }; } char LineProfiler::ID = 0; INITIALIZE_PASS(LineProfiler, "insert-line-profiling", "Insert instrumentation for line profiling", false, false) ModulePass *llvm::createLineProfilerPass() { return new LineProfiler(); } bool LineProfiler::runOnModule(Module &M) { Mod = &M; Ctx = &M.getContext(); DebugLoc last_line; // initializes to unknown bool Changed = false; for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) { for (inst_iterator II = inst_begin(F), IE = inst_end(F); II != IE; ++II) { const DebugLoc &loc = II->getDebugLoc(); if (loc.isUnknown()) continue; if (loc == last_line) continue; last_line = loc; InsertCounterUpdateBefore(&*II); ++NumUpdatesInserted; Changed = true; } } if (Changed) { InsertCounterWriteout(); } return Changed; } void LineProfiler::InsertCounterUpdateBefore(Instruction *I) { const DebugLoc &loc = I->getDebugLoc(); GlobalVariable *&counter = counters[loc]; const Type *Int64Ty = Type::getInt64Ty(*Ctx); if (!counter) { counter = new GlobalVariable(*Mod, Int64Ty, false, GlobalValue::InternalLinkage, Constant::getNullValue(Int64Ty), "__llvm_prof_linecov_ctr", 0, false, 0); counter->setVisibility(GlobalVariable::HiddenVisibility); counter->setUnnamedAddr(true); } if (isa(I)) { // We may not error out or crash in this case, because a module could put // changing line numbers on phi nodes and still pass the verifier. dbgs() << "Refusing to insert code before phi: " << *I << "\n"; I = I->getParent()->getFirstNonPHI(); } IRBuilder<> builder(I); Value *ctr = builder.CreateLoad(counter); ctr = builder.CreateAdd(ctr, ConstantInt::get(Int64Ty, 1)); builder.CreateStore(ctr, counter); } static DISubprogram FindSubprogram(DIScope scope) { while (!scope.isSubprogram()) { assert(scope.isLexicalBlock() && "Debug location not lexical block or subprogram"); scope = DILexicalBlock(scope).getContext(); } return DISubprogram(scope); } Constant *LineProfiler::getStartFileFunc() { const Type *Args[1] = { Type::getInt8PtrTy(*Ctx) }; const FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), Args, false); return Mod->getOrInsertFunction("llvm_prof_linectr_start_file", FTy); } Constant *LineProfiler::getCounterFunc() { const Type *Args[] = { Type::getInt8PtrTy(*Ctx), // const char *dir Type::getInt8PtrTy(*Ctx), // const char *file Type::getInt32Ty(*Ctx), // uint32_t line Type::getInt32Ty(*Ctx), // uint32_t column Type::getInt64PtrTy(*Ctx), // int64_t *counter }; const FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), Args, false); return Mod->getOrInsertFunction("llvm_prof_linectr_emit_counter", FTy); } Constant *LineProfiler::getEndFileFunc() { const FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), false); return Mod->getOrInsertFunction("llvm_prof_linectr_end_file", FTy); } void LineProfiler::InsertCounterWriteout() { std::set compile_units; for (DenseMap::iterator I = counters.begin(), E = counters.end(); I != E; ++I) { const DebugLoc &loc = I->first; DISubprogram subprogram(FindSubprogram(DIScope(loc.getScope(*Ctx)))); compile_units.insert(subprogram.getCompileUnit().getFilename().str()); } const FunctionType *WriteoutFTy = FunctionType::get(Type::getVoidTy(*Ctx), false); Function *WriteoutF = Function::Create(WriteoutFTy, GlobalValue::InternalLinkage, "__llvm_prof_linecov_dtor", Mod); WriteoutF->setUnnamedAddr(true); BasicBlock *BB = BasicBlock::Create(*Ctx, "", WriteoutF); IRBuilder<> builder(BB); Constant *StartFile = getStartFileFunc(); Constant *EmitCounter = getCounterFunc(); Constant *EndFile = getEndFileFunc(); for (std::set::const_iterator CUI = compile_units.begin(), CUE = compile_units.end(); CUI != CUE; ++CUI) { builder.CreateCall(StartFile, builder.CreateGlobalStringPtr(*CUI)); for (DenseMap::iterator I = counters.begin(), E = counters.end(); I != E; ++I) { const DebugLoc &loc = I->first; DISubprogram subprogram(FindSubprogram(DIScope(loc.getScope(*Ctx)))); DICompileUnit compileunit(subprogram.getCompileUnit()); if (compileunit.getFilename() != *CUI) continue; Value *Args[] = { builder.CreateGlobalStringPtr(subprogram.getDirectory()), builder.CreateGlobalStringPtr(subprogram.getFilename()), ConstantInt::get(Type::getInt32Ty(*Ctx), loc.getLine()), ConstantInt::get(Type::getInt32Ty(*Ctx), loc.getCol()), I->second }; builder.CreateCall(EmitCounter, Args); } builder.CreateCall(EndFile); } builder.CreateRetVoid(); InsertProfilingShutdownCall(WriteoutF, Mod); }