blob: 5628c3a8110821bf033217a16870c98c40155a60 [file] [log] [blame]
//===- 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);
}