From b1928704201034c785a26296a49f69355eb56a05 Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Sat, 16 Apr 2011 01:20:23 +0000 Subject: [PATCH] Rename LineProfiling to GCOVProfiling to more accurately represent what it does. Also mostly implement it. Still a work-in-progress, but generates legal output on crafted test cases. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@129630 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/InitializePasses.h | 2 +- include/llvm/LinkAllPasses.h | 2 +- include/llvm/Transforms/Instrumentation.h | 4 +- .../Instrumentation/GCOVProfiling.cpp | 508 ++++++++++++++++++ .../Instrumentation/Instrumentation.cpp | 2 +- .../Instrumentation/LineProfiling.cpp | 217 -------- runtime/libprofile/GCDAProfiling.c | 104 ++++ runtime/libprofile/LineProfiling.c | 40 -- runtime/libprofile/libprofile.exports | 7 +- 9 files changed, 621 insertions(+), 265 deletions(-) create mode 100644 lib/Transforms/Instrumentation/GCOVProfiling.cpp delete mode 100644 lib/Transforms/Instrumentation/LineProfiling.cpp create mode 100644 runtime/libprofile/GCDAProfiling.c delete mode 100644 runtime/libprofile/LineProfiling.c diff --git a/include/llvm/InitializePasses.h b/include/llvm/InitializePasses.h index be71898af44..cca0194a60e 100644 --- a/include/llvm/InitializePasses.h +++ b/include/llvm/InitializePasses.h @@ -94,7 +94,7 @@ void initializeDominatorTreePass(PassRegistry&); void initializeEdgeBundlesPass(PassRegistry&); void initializeEdgeProfilerPass(PassRegistry&); void initializePathProfilerPass(PassRegistry&); -void initializeLineProfilerPass(PassRegistry&); +void initializeGCOVProfilerPass(PassRegistry&); void initializeEarlyCSEPass(PassRegistry&); void initializeExpandISelPseudosPass(PassRegistry&); void initializeFindUsedTypesPass(PassRegistry&); diff --git a/include/llvm/LinkAllPasses.h b/include/llvm/LinkAllPasses.h index 9c486b0181d..56c113b4528 100644 --- a/include/llvm/LinkAllPasses.h +++ b/include/llvm/LinkAllPasses.h @@ -70,7 +70,7 @@ namespace { (void) llvm::createEdgeProfilerPass(); (void) llvm::createOptimalEdgeProfilerPass(); (void) llvm::createPathProfilerPass(); - (void) llvm::createLineProfilerPass(); + (void) llvm::createGCOVProfilerPass(); (void) llvm::createFunctionInliningPass(); (void) llvm::createAlwaysInlinerPass(); (void) llvm::createGlobalDCEPass(); diff --git a/include/llvm/Transforms/Instrumentation.h b/include/llvm/Transforms/Instrumentation.h index d47216dec72..3ef2f5a14d0 100644 --- a/include/llvm/Transforms/Instrumentation.h +++ b/include/llvm/Transforms/Instrumentation.h @@ -27,8 +27,8 @@ ModulePass *createOptimalEdgeProfilerPass(); // Insert path profiling instrumentation ModulePass *createPathProfilerPass(); -// Insert line profiling instrumentation -ModulePass *createLineProfilerPass(); +// Insert GCOV profiling instrumentation +ModulePass *createGCOVProfilerPass(); } // End llvm namespace diff --git a/lib/Transforms/Instrumentation/GCOVProfiling.cpp b/lib/Transforms/Instrumentation/GCOVProfiling.cpp new file mode 100644 index 00000000000..ec05872179e --- /dev/null +++ b/lib/Transforms/Instrumentation/GCOVProfiling.cpp @@ -0,0 +1,508 @@ +//===- 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 +#include +using namespace llvm; + +namespace { + class GCOVProfiler : public ModulePass { + bool runOnModule(Module &M); + public: + static char ID; + GCOVProfiler() : ModulePass(ID) { + 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); + + // 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, 8> &); + + Module *Mod; + LLVMContext *Ctx; + }; +} + +char GCOVProfiler::ID = 0; +INITIALIZE_PASS(GCOVProfiler, "insert-gcov-profiling", + "Insert instrumentation for GCOV profiling", false, false) + +ModulePass *llvm::createGCOVProfilerPass() { return new GCOVProfiler(); } + +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(&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 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::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::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 lines_by_file; + SmallVector 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); + } + + 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((MDNode*)SP); + Write(ident); + Write(0); // checksum + WriteGCOVString(SP.getName()); + WriteGCOVString(SP.getFilename()); + Write(SP.getLineNumber()); + } + + ~GCOVFunction() { + DeleteContainerSeconds(blocks); + } + + GCOVBlock &GetBlock(BasicBlock *BB) { + return *blocks[BB]; + } + + void WriteOut() { + // Emit count of blocks. + WriteBytes(block_tag, 4); + Write(blocks.size()); + for (int i = 0, e = blocks.size(); i != e; ++i) { + Write(0); // No flags on our blocks. + } + + // Emit edges between blocks. + for (DenseMap::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::iterator I = blocks.begin(), + E = blocks.end(); I != E; ++I) { + I->second->WriteOut(); + } + } + + private: + DenseMap blocks; + }; +} + +void GCOVProfiler::EmitGCNO(DebugInfoFinder &DIF) { + DenseMap 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( + (sys::path::stem(CU.getFilename()) + ".gcno").str().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))); + } + } + + 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::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", 4); // EOF + Out->close(); + delete Out; + } +} + +bool GCOVProfiler::runOnModule(Module &M) { + Mod = &M; + Ctx = &M.getContext(); + + DebugInfoFinder DIF; + DIF.processModule(*Mod); + + EmitGCNO(DIF); + + SmallVector, 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(); + + // TODO: GCOV format requires a distinct unified exit block. + unsigned edges = 0; + for (Function::iterator BB = F->begin(), E = F->end(); BB != E; ++BB) { + TerminatorInst *TI = BB->getTerminator(); + 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((MDNode*)SP))); + + UniqueVector complex_edge_preds; + UniqueVector complex_edge_succs; + + unsigned edge_num = 0; + for (Function::iterator BB = F->begin(), E = F->end(); BB != E; ++BB) { + TerminatorInst *TI = BB->getTerminator(); + if (int successors = TI->getNumSuccessors()) { + 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(TI)) { + Value *sel = builder.CreateSelect( + BI->getCondition(), + ConstantInt::get(Type::getInt64Ty(*Ctx), edge_num), + ConstantInt::get(Type::getInt64Ty(*Ctx), edge_num + 1)); + SmallVector 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); +} + +static std::string ReplaceStem(std::string orig_filename, std::string new_stem){ + return (sys::path::stem(orig_filename) + "." + new_stem).str(); +} + +void GCOVProfiler::InsertCounterWriteout( + DebugInfoFinder &DIF, + SmallVector, 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, 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(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); +} diff --git a/lib/Transforms/Instrumentation/Instrumentation.cpp b/lib/Transforms/Instrumentation/Instrumentation.cpp index b299bb71a4a..71adc1ec6de 100644 --- a/lib/Transforms/Instrumentation/Instrumentation.cpp +++ b/lib/Transforms/Instrumentation/Instrumentation.cpp @@ -23,7 +23,7 @@ void llvm::initializeInstrumentation(PassRegistry &Registry) { initializeEdgeProfilerPass(Registry); initializeOptimalEdgeProfilerPass(Registry); initializePathProfilerPass(Registry); - initializeLineProfilerPass(Registry); + initializeGCOVProfilerPass(Registry); } /// LLVMInitializeInstrumentation - C binding for diff --git a/lib/Transforms/Instrumentation/LineProfiling.cpp b/lib/Transforms/Instrumentation/LineProfiling.cpp deleted file mode 100644 index 5628c3a8110..00000000000 --- a/lib/Transforms/Instrumentation/LineProfiling.cpp +++ /dev/null @@ -1,217 +0,0 @@ -//===- 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); -} diff --git a/runtime/libprofile/GCDAProfiling.c b/runtime/libprofile/GCDAProfiling.c new file mode 100644 index 00000000000..ee48ab5b6b0 --- /dev/null +++ b/runtime/libprofile/GCDAProfiling.c @@ -0,0 +1,104 @@ +/*===- GCDAProfiling.c - Support library for GCDA file emission -----------===*\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +|*===----------------------------------------------------------------------===*| +|* +|* This file implements the call back routines for the gcov profiling +|* instrumentation pass. Link against this library when running code through +|* the -insert-gcov-profiling LLVM pass. +|* +|* We emit files in a corrupt version of GCOV's "gcda" file format. These files +|* are only close enough that LCOV will happily parse them. Anything that lcov +|* ignores is missing. +|* +\*===----------------------------------------------------------------------===*/ + +#include "llvm/Support/DataTypes.h" +#include +#include +#include +#include + +// #define DEBUG_GCDAPROFILING + +/* + * --- GCOV file format I/O primitives --- + */ + +static FILE *output_file = NULL; + +static void write_int32(uint32_t i) { + fwrite(&i, 4, 1, output_file); +} + +static void write_int64(uint64_t i) { + uint32_t lo, hi; + lo = i & 0x00000000ffffffff; + hi = i & 0xffffffff00000000; + + write_int32(lo); + write_int32(hi); +} + +/* + * --- LLVM line counter API --- + */ + +/* A file in this case is a translation unit. Each .o file built with line + * profiling enabled will emit to a different file. Only one file may be + * started at a time. + */ +void llvm_gcda_start_file(const char *filename) { + output_file = fopen(filename, "w+"); + + /* gcda file, version 404*, stamp LLVM. */ + fwrite("adcg*404MVLL", 12, 1, output_file); + +#ifdef DEBUG_GCDAPROFILING + printf("[%s]\n", filename); +#endif +} + +void llvm_gcda_emit_function(uint32_t ident) { +#ifdef DEBUG_GCDAPROFILING + printf("function id=%x\n", ident); +#endif + + /* function tag */ + fwrite("\0\0\0\1", 4, 1, output_file); + write_int32(2); + write_int32(ident); + write_int32(0); +} + +void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) { + uint32_t i; + /* counter #1 (arcs) tag */ + fwrite("\0\0\xa1\1", 4, 1, output_file); + write_int32(num_counters * 2); + for (i = 0; i < num_counters; ++i) { + write_int64(counters[i]); + } + +#ifdef DEBUG_GCDAPROFILING + printf(" %u arcs\n", num_counters); + for (i = 0; i < num_counters; ++i) { + printf(" %llu\n", (unsigned long long)counters[i]); + } +#endif +} + +void llvm_gcda_end_file() { + /* Write out EOF record. */ + fwrite("\0\0\0\0\0\0\0\0", 8, 1, output_file); + fclose(output_file); + output_file = NULL; + +#ifdef DEBUG_GCDAPROFILING + printf("-----\n"); +#endif +} diff --git a/runtime/libprofile/LineProfiling.c b/runtime/libprofile/LineProfiling.c deleted file mode 100644 index 9db789ec0b5..00000000000 --- a/runtime/libprofile/LineProfiling.c +++ /dev/null @@ -1,40 +0,0 @@ -/*===- LineProfiling.c - Support library 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 file implements the call back routines for the line profiling -|* instrumentation pass. Link against this library when running code through -|* the -insert-line-profiling LLVM pass. -|* -\*===----------------------------------------------------------------------===*/ - -#include -#include -#include - -#include "llvm/Support/DataTypes.h" - -/* A file in this case is a translation unit. Each .o file built with line - * profiling enabled will emit to a different file. Only one file may be - * started at a time. - */ -void llvm_prof_linectr_start_file(const char *orig_filename) { - printf("[%s]\n", orig_filename); -} - -/* Emit data about a counter to the data file. */ -void llvm_prof_linectr_emit_counter(const char *dir, const char *file, - uint32_t line, uint32_t column, - uint64_t *counter) { - printf("%s/%s:%u:%u %llu\n", dir, file, line, column, - (unsigned long long)(*counter)); -} - -void llvm_prof_linectr_end_file() { - printf("-----\n"); -} diff --git a/runtime/libprofile/libprofile.exports b/runtime/libprofile/libprofile.exports index fb04ea3480d..aca563fc5ca 100644 --- a/runtime/libprofile/libprofile.exports +++ b/runtime/libprofile/libprofile.exports @@ -5,6 +5,7 @@ llvm_start_basic_block_tracing llvm_trace_basic_block llvm_increment_path_count llvm_decrement_path_count -llvm_prof_linectr_start_file -llvm_prof_linectr_emit_counter -llvm_prof_linectr_end_file +llvm_gcda_start_file +llvm_gcda_emit_function +llvm_gcda_emit_arcs +llvm_gcda_end_file