diff options
Diffstat (limited to 'lib/Transforms/Instrumentation/LineProfiling.cpp')
-rw-r--r-- | lib/Transforms/Instrumentation/LineProfiling.cpp | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/lib/Transforms/Instrumentation/LineProfiling.cpp b/lib/Transforms/Instrumentation/LineProfiling.cpp new file mode 100644 index 0000000000..5628c3a811 --- /dev/null +++ b/lib/Transforms/Instrumentation/LineProfiling.cpp @@ -0,0 +1,217 @@ +//===- 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); +} |