llvm-6502/lib/Transforms/Instrumentation/GCOVProfiling.cpp
Nick Lewycky a4c4c0e129 In gcov profiling, give all functions an extra unified return block. This is
necessary since gcov counts transitions between blocks. It can't see if you've
run every line in a straight-line function, so we add an edge for it to notice.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@129905 91177308-0d34-0410-b5e6-96231b3b80d8
2011-04-21 03:18:00 +00:00

543 lines
18 KiB
C++

//===- GCOVProfiling.cpp - Insert edge counters for gcov profiling --------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass implements GCOV-style profiling. When this pass is run it emits
// "gcno" files next to the existing source, and instruments the code that runs
// to records the edges between blocks that run and emit a complementary "gcda"
// file on exit.
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "insert-gcov-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/Support/PathV2.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/UniqueVector.h"
#include <string>
#include <utility>
using namespace llvm;
namespace {
class GCOVProfiler : public ModulePass {
bool runOnModule(Module &M);
public:
static char ID;
GCOVProfiler()
: ModulePass(ID), EmitNotes(true), EmitData(true) {
initializeGCOVProfilerPass(*PassRegistry::getPassRegistry());
}
GCOVProfiler(bool EmitNotes, bool EmitData)
: ModulePass(ID), EmitNotes(EmitNotes), EmitData(EmitData) {
assert((EmitNotes || EmitData) && "GCOVProfiler asked to do nothing?");
initializeGCOVProfilerPass(*PassRegistry::getPassRegistry());
}
virtual const char *getPassName() const {
return "GCOV Profiler";
}
private:
// Create the GCNO files for the Module based on DebugInfo.
void EmitGCNO(DebugInfoFinder &DIF);
// Modify the program to track transitions along edges and call into the
// profiling runtime to emit .gcda files when run.
bool EmitProfileArcs(DebugInfoFinder &DIF);
// Get pointers to the functions in the runtime library.
Constant *getStartFileFunc();
Constant *getEmitFunctionFunc();
Constant *getEmitArcsFunc();
Constant *getEndFileFunc();
// Add the function to write out all our counters to the global destructor
// list.
void InsertCounterWriteout(DebugInfoFinder &,
SmallVector<std::pair<GlobalVariable *,
uint32_t>, 8> &);
bool EmitNotes;
bool EmitData;
Module *Mod;
LLVMContext *Ctx;
};
}
char GCOVProfiler::ID = 0;
INITIALIZE_PASS(GCOVProfiler, "insert-gcov-profiling",
"Insert instrumentation for GCOV profiling", false, false)
ModulePass *llvm::createGCOVProfilerPass(bool EmitNotes, bool EmitData) {
return new GCOVProfiler(EmitNotes, EmitData);
}
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);
}
namespace {
class GCOVRecord {
protected:
static const char *lines_tag;
static const char *function_tag;
static const char *block_tag;
static const char *edge_tag;
GCOVRecord() {}
void WriteBytes(const char *b, int size) {
os->write(b, size);
}
void Write(uint32_t i) {
WriteBytes(reinterpret_cast<char*>(&i), 4);
}
// Returns the length measured in 4-byte blocks that will be used to
// represent this string in a GCOV file
unsigned LengthOfGCOVString(StringRef s) {
// A GCOV string is a length, followed by a NUL, then between 0 and 3 NULs
// padding out to the next 4-byte word. The length is measured in 4-byte
// words including padding, not bytes of actual string.
return (s.size() + 5) / 4;
}
void WriteGCOVString(StringRef s) {
uint32_t len = LengthOfGCOVString(s);
Write(len);
WriteBytes(s.data(), s.size());
// Write 1 to 4 bytes of NUL padding.
assert((unsigned)(5 - ((s.size() + 1) % 4)) > 0);
assert((unsigned)(5 - ((s.size() + 1) % 4)) <= 4);
WriteBytes("\0\0\0\0", 5 - ((s.size() + 1) % 4));
}
raw_ostream *os;
};
const char *GCOVRecord::lines_tag = "\0\0\x45\x01";
const char *GCOVRecord::function_tag = "\0\0\0\1";
const char *GCOVRecord::block_tag = "\0\0\x41\x01";
const char *GCOVRecord::edge_tag = "\0\0\x43\x01";
class GCOVFunction;
class GCOVBlock;
// Constructed only by requesting it from a GCOVBlock, this object stores a
// list of line numbers and a single filename, representing lines that belong
// to the block.
class GCOVLines : public GCOVRecord {
public:
void AddLine(uint32_t line) {
lines.push_back(line);
}
uint32_t Length() {
return LengthOfGCOVString(filename) + 2 + lines.size();
}
private:
friend class GCOVBlock;
GCOVLines(std::string filename, raw_ostream *os)
: filename(filename) {
this->os = os;
}
std::string filename;
SmallVector<uint32_t, 32> lines;
};
// Represent a basic block in GCOV. Each block has a unique number in the
// function, number of lines belonging to each block, and a set of edges to
// other blocks.
class GCOVBlock : public GCOVRecord {
public:
GCOVLines &GetFile(std::string filename) {
GCOVLines *&lines = lines_by_file[filename];
if (!lines) {
lines = new GCOVLines(filename, os);
}
return *lines;
}
void AddEdge(GCOVBlock &successor) {
out_edges.push_back(&successor);
}
void WriteOut() {
uint32_t len = 3;
for (StringMap<GCOVLines *>::iterator I = lines_by_file.begin(),
E = lines_by_file.end(); I != E; ++I) {
len += I->second->Length();
}
WriteBytes(lines_tag, 4);
Write(len);
Write(number);
for (StringMap<GCOVLines *>::iterator I = lines_by_file.begin(),
E = lines_by_file.end(); I != E; ++I) {
Write(0);
WriteGCOVString(I->second->filename);
for (int i = 0, e = I->second->lines.size(); i != e; ++i) {
Write(I->second->lines[i]);
}
}
Write(0);
Write(0);
}
~GCOVBlock() {
DeleteContainerSeconds(lines_by_file);
}
private:
friend class GCOVFunction;
GCOVBlock(uint32_t number, raw_ostream *os)
: number(number) {
this->os = os;
}
uint32_t number;
BasicBlock *block;
StringMap<GCOVLines *> lines_by_file;
SmallVector<GCOVBlock *, 4> out_edges;
};
// A function has a unique identifier, a checksum (we leave as zero) and a
// set of blocks and a map of edges between blocks. This is the only GCOV
// object users can construct, the blocks and lines will be rooted here.
class GCOVFunction : public GCOVRecord {
public:
GCOVFunction(DISubprogram SP, raw_ostream *os) {
this->os = os;
Function *F = SP.getFunction();
uint32_t i = 0;
for (Function::iterator BB = F->begin(), E = F->end(); BB != E; ++BB) {
blocks[BB] = new GCOVBlock(i++, os);
}
return_block = new GCOVBlock(i++, os);
WriteBytes(function_tag, 4);
uint32_t block_len = 1 + 1 + 1 + LengthOfGCOVString(SP.getName()) +
1 + LengthOfGCOVString(SP.getFilename()) + 1;
Write(block_len);
uint32_t ident = reinterpret_cast<intptr_t>((MDNode*)SP);
Write(ident);
Write(0); // checksum
WriteGCOVString(SP.getName());
WriteGCOVString(SP.getFilename());
Write(SP.getLineNumber());
}
~GCOVFunction() {
DeleteContainerSeconds(blocks);
delete return_block;
}
GCOVBlock &GetBlock(BasicBlock *BB) {
return *blocks[BB];
}
GCOVBlock &GetReturnBlock() {
return *return_block;
}
void WriteOut() {
// Emit count of blocks.
WriteBytes(block_tag, 4);
Write(blocks.size() + 1);
for (int i = 0, e = blocks.size() + 1; i != e; ++i) {
Write(0); // No flags on our blocks.
}
// Emit edges between blocks.
for (DenseMap<BasicBlock *, GCOVBlock *>::iterator I = blocks.begin(),
E = blocks.end(); I != E; ++I) {
GCOVBlock &block = *I->second;
if (block.out_edges.empty()) continue;
WriteBytes(edge_tag, 4);
Write(block.out_edges.size() * 2 + 1);
Write(block.number);
for (int i = 0, e = block.out_edges.size(); i != e; ++i) {
Write(block.out_edges[i]->number);
Write(0); // no flags
}
}
// Emit lines for each block.
for (DenseMap<BasicBlock *, GCOVBlock *>::iterator I = blocks.begin(),
E = blocks.end(); I != E; ++I) {
I->second->WriteOut();
}
}
private:
DenseMap<BasicBlock *, GCOVBlock *> blocks;
GCOVBlock *return_block;
};
}
// Replace the stem of a file, or add one if missing.
static std::string ReplaceStem(std::string orig_filename, std::string new_stem){
return (sys::path::stem(orig_filename) + "." + new_stem).str();
}
bool GCOVProfiler::runOnModule(Module &M) {
Mod = &M;
Ctx = &M.getContext();
DebugInfoFinder DIF;
DIF.processModule(*Mod);
if (EmitNotes) EmitGCNO(DIF);
if (EmitData) return EmitProfileArcs(DIF);
return false;
}
void GCOVProfiler::EmitGCNO(DebugInfoFinder &DIF) {
DenseMap<const MDNode *, raw_fd_ostream *> gcno_files;
for (DebugInfoFinder::iterator I = DIF.compile_unit_begin(),
E = DIF.compile_unit_end(); I != E; ++I) {
// Each compile unit gets its own .gcno file. This means that whether we run
// this pass over the original .o's as they're produced, or run it after
// LTO, we'll generate the same .gcno files.
DICompileUnit CU(*I);
raw_fd_ostream *&Out = gcno_files[CU];
std::string ErrorInfo;
Out = new raw_fd_ostream(ReplaceStem(CU.getFilename(), "gcno").c_str(),
ErrorInfo, raw_fd_ostream::F_Binary);
Out->write("oncg*404MVLL", 12);
}
for (DebugInfoFinder::iterator SPI = DIF.subprogram_begin(),
SPE = DIF.subprogram_end(); SPI != SPE; ++SPI) {
DISubprogram SP(*SPI);
raw_fd_ostream *&os = gcno_files[SP.getCompileUnit()];
GCOVFunction function(SP, os);
Function *F = SP.getFunction();
for (Function::iterator BB = F->begin(), E = F->end(); BB != E; ++BB) {
GCOVBlock &block = function.GetBlock(BB);
TerminatorInst *TI = BB->getTerminator();
if (int successors = TI->getNumSuccessors()) {
for (int i = 0; i != successors; ++i) {
block.AddEdge(function.GetBlock(TI->getSuccessor(i)));
}
} else if (isa<ReturnInst>(TI)) {
block.AddEdge(function.GetReturnBlock());
}
uint32_t line = 0;
for (BasicBlock::iterator I = BB->begin(), IE = BB->end(); I != IE; ++I) {
const DebugLoc &loc = I->getDebugLoc();
if (loc.isUnknown()) continue;
if (line == loc.getLine()) continue;
line = loc.getLine();
if (SP != FindSubprogram(DIScope(loc.getScope(*Ctx)))) continue;
GCOVLines &lines = block.GetFile(SP.getFilename());
lines.AddLine(loc.getLine());
}
}
function.WriteOut();
}
for (DenseMap<const MDNode *, raw_fd_ostream *>::iterator
I = gcno_files.begin(), E = gcno_files.end(); I != E; ++I) {
raw_fd_ostream *&Out = I->second;
Out->write("\0\0\0\0\0\0\0\0", 8); // EOF
Out->close();
delete Out;
}
}
bool GCOVProfiler::EmitProfileArcs(DebugInfoFinder &DIF) {
if (DIF.subprogram_begin() == DIF.subprogram_end())
return false;
SmallVector<std::pair<GlobalVariable *, uint32_t>, 8> counters_by_ident;
for (DebugInfoFinder::iterator SPI = DIF.subprogram_begin(),
SPE = DIF.subprogram_end(); SPI != SPE; ++SPI) {
DISubprogram SP(*SPI);
Function *F = SP.getFunction();
unsigned edges = 0;
for (Function::iterator BB = F->begin(), E = F->end(); BB != E; ++BB) {
TerminatorInst *TI = BB->getTerminator();
if (isa<ReturnInst>(TI))
++edges;
else
edges += TI->getNumSuccessors();
}
const ArrayType *counter_type =
ArrayType::get(Type::getInt64Ty(*Ctx), edges);
GlobalVariable *counter =
new GlobalVariable(*Mod, counter_type, false,
GlobalValue::InternalLinkage,
Constant::getNullValue(counter_type),
"__llvm_gcov_ctr", 0, false, 0);
counters_by_ident.push_back(
std::make_pair(counter, reinterpret_cast<intptr_t>((MDNode*)SP)));
UniqueVector<BasicBlock *> complex_edge_preds;
UniqueVector<BasicBlock *> complex_edge_succs;
unsigned edge_num = 0;
for (Function::iterator BB = F->begin(), E = F->end(); BB != E; ++BB) {
TerminatorInst *TI = BB->getTerminator();
int successors = isa<ReturnInst>(TI) ? 1 : TI->getNumSuccessors();
if (successors) {
IRBuilder<> builder(TI);
if (successors == 1) {
Value *ctr = builder.CreateConstInBoundsGEP2_64(counter, 0, edge_num);
Value *count = builder.CreateLoad(ctr);
count = builder.CreateAdd(count,
ConstantInt::get(Type::getInt64Ty(*Ctx),1));
builder.CreateStore(count, ctr);
} else if (BranchInst *BI = dyn_cast<BranchInst>(TI)) {
Value *sel = builder.CreateSelect(
BI->getCondition(),
ConstantInt::get(Type::getInt64Ty(*Ctx), edge_num),
ConstantInt::get(Type::getInt64Ty(*Ctx), edge_num + 1));
SmallVector<Value *, 2> idx;
idx.push_back(Constant::getNullValue(Type::getInt64Ty(*Ctx)));
idx.push_back(sel);
Value *ctr = builder.CreateInBoundsGEP(counter,
idx.begin(), idx.end());
Value *count = builder.CreateLoad(ctr);
count = builder.CreateAdd(count,
ConstantInt::get(Type::getInt64Ty(*Ctx),1));
builder.CreateStore(count, ctr);
} else {
complex_edge_preds.insert(BB);
for (int i = 0; i != successors; ++i) {
complex_edge_succs.insert(TI->getSuccessor(i));
}
}
edge_num += successors;
}
}
// TODO: support switch, invoke, indirectbr
if (!complex_edge_preds.empty()) {
// emit a [preds x [succs x i64*]].
for (int i = 0, e = complex_edge_preds.size(); i != e; ++i) {
// call runtime to state save
}
for (int i = 0, e = complex_edge_succs.size(); i != e; ++i) {
// call runtime to perform increment
}
}
}
InsertCounterWriteout(DIF, counters_by_ident);
return true;
}
Constant *GCOVProfiler::getStartFileFunc() {
const Type *Args[1] = { Type::getInt8PtrTy(*Ctx) };
const FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx),
Args, false);
return Mod->getOrInsertFunction("llvm_gcda_start_file", FTy);
}
Constant *GCOVProfiler::getEmitFunctionFunc() {
const Type *Args[1] = { Type::getInt32Ty(*Ctx) };
const FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx),
Args, false);
return Mod->getOrInsertFunction("llvm_gcda_emit_function", FTy);
}
Constant *GCOVProfiler::getEmitArcsFunc() {
const Type *Args[] = {
Type::getInt32Ty(*Ctx), // uint32_t num_counters
Type::getInt64PtrTy(*Ctx), // uint64_t *counters
};
const FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx),
Args, false);
return Mod->getOrInsertFunction("llvm_gcda_emit_arcs", FTy);
}
Constant *GCOVProfiler::getEndFileFunc() {
const FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), false);
return Mod->getOrInsertFunction("llvm_gcda_end_file", FTy);
}
void GCOVProfiler::InsertCounterWriteout(
DebugInfoFinder &DIF,
SmallVector<std::pair<GlobalVariable *, uint32_t>, 8> &counters_by_ident) {
const FunctionType *WriteoutFTy =
FunctionType::get(Type::getVoidTy(*Ctx), false);
Function *WriteoutF = Function::Create(WriteoutFTy,
GlobalValue::InternalLinkage,
"__llvm_gcda_writeout", Mod);
WriteoutF->setUnnamedAddr(true);
BasicBlock *BB = BasicBlock::Create(*Ctx, "", WriteoutF);
IRBuilder<> builder(BB);
Constant *StartFile = getStartFileFunc();
Constant *EmitFunction = getEmitFunctionFunc();
Constant *EmitArcs = getEmitArcsFunc();
Constant *EndFile = getEndFileFunc();
for (DebugInfoFinder::iterator CUI = DIF.compile_unit_begin(),
CUE = DIF.compile_unit_end(); CUI != CUE; ++CUI) {
DICompileUnit compile_unit(*CUI);
std::string filename_gcda = ReplaceStem(compile_unit.getFilename(), "gcda");
builder.CreateCall(StartFile,
builder.CreateGlobalStringPtr(filename_gcda));
for (SmallVector<std::pair<GlobalVariable *, uint32_t>, 8>::iterator
I = counters_by_ident.begin(), E = counters_by_ident.end();
I != E; ++I) {
builder.CreateCall(EmitFunction, ConstantInt::get(Type::getInt32Ty(*Ctx),
I->second));
GlobalVariable *GV = I->first;
unsigned num_arcs =
cast<ArrayType>(GV->getType()->getElementType())->getNumElements();
builder.CreateCall2(
EmitArcs,
ConstantInt::get(Type::getInt32Ty(*Ctx), num_arcs),
builder.CreateConstGEP2_64(GV, 0, 0));
}
builder.CreateCall(EndFile);
}
builder.CreateRetVoid();
InsertProfilingShutdownCall(WriteoutF, Mod);
}