| //===- 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 <set> |
| #include <string> |
| 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<DebugLoc, GlobalVariable *> 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<PHINode>(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<std::string> compile_units; |
| for (DenseMap<DebugLoc, GlobalVariable *>::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<std::string>::const_iterator CUI = compile_units.begin(), |
| CUE = compile_units.end(); CUI != CUE; ++CUI) { |
| builder.CreateCall(StartFile, |
| builder.CreateGlobalStringPtr(*CUI)); |
| for (DenseMap<DebugLoc, GlobalVariable *>::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); |
| } |