|  | //===- GCOVProfiling.cpp - Insert edge counters for gcov profiling --------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // 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. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "llvm/ADT/DenseMap.h" | 
|  | #include "llvm/ADT/Hashing.h" | 
|  | #include "llvm/ADT/STLExtras.h" | 
|  | #include "llvm/ADT/Sequence.h" | 
|  | #include "llvm/ADT/Statistic.h" | 
|  | #include "llvm/ADT/StringExtras.h" | 
|  | #include "llvm/ADT/StringMap.h" | 
|  | #include "llvm/Analysis/EHPersonalities.h" | 
|  | #include "llvm/Analysis/TargetLibraryInfo.h" | 
|  | #include "llvm/IR/CFG.h" | 
|  | #include "llvm/IR/DebugInfo.h" | 
|  | #include "llvm/IR/DebugLoc.h" | 
|  | #include "llvm/IR/IRBuilder.h" | 
|  | #include "llvm/IR/InstIterator.h" | 
|  | #include "llvm/IR/Instructions.h" | 
|  | #include "llvm/IR/IntrinsicInst.h" | 
|  | #include "llvm/IR/Module.h" | 
|  | #include "llvm/InitializePasses.h" | 
|  | #include "llvm/Pass.h" | 
|  | #include "llvm/Support/CommandLine.h" | 
|  | #include "llvm/Support/Debug.h" | 
|  | #include "llvm/Support/FileSystem.h" | 
|  | #include "llvm/Support/Path.h" | 
|  | #include "llvm/Support/Regex.h" | 
|  | #include "llvm/Support/raw_ostream.h" | 
|  | #include "llvm/Transforms/Instrumentation.h" | 
|  | #include "llvm/Transforms/Instrumentation/GCOVProfiler.h" | 
|  | #include "llvm/Transforms/Utils/ModuleUtils.h" | 
|  | #include <algorithm> | 
|  | #include <memory> | 
|  | #include <string> | 
|  | #include <utility> | 
|  | using namespace llvm; | 
|  |  | 
|  | #define DEBUG_TYPE "insert-gcov-profiling" | 
|  |  | 
|  | static cl::opt<std::string> | 
|  | DefaultGCOVVersion("default-gcov-version", cl::init("402*"), cl::Hidden, | 
|  | cl::ValueRequired); | 
|  | static cl::opt<bool> DefaultExitBlockBeforeBody("gcov-exit-block-before-body", | 
|  | cl::init(false), cl::Hidden); | 
|  |  | 
|  | GCOVOptions GCOVOptions::getDefault() { | 
|  | GCOVOptions Options; | 
|  | Options.EmitNotes = true; | 
|  | Options.EmitData = true; | 
|  | Options.UseCfgChecksum = false; | 
|  | Options.NoRedZone = false; | 
|  | Options.FunctionNamesInData = true; | 
|  | Options.ExitBlockBeforeBody = DefaultExitBlockBeforeBody; | 
|  |  | 
|  | if (DefaultGCOVVersion.size() != 4) { | 
|  | llvm::report_fatal_error(std::string("Invalid -default-gcov-version: ") + | 
|  | DefaultGCOVVersion); | 
|  | } | 
|  | memcpy(Options.Version, DefaultGCOVVersion.c_str(), 4); | 
|  | return Options; | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | class GCOVFunction; | 
|  |  | 
|  | class GCOVProfiler { | 
|  | public: | 
|  | GCOVProfiler() : GCOVProfiler(GCOVOptions::getDefault()) {} | 
|  | GCOVProfiler(const GCOVOptions &Opts) : Options(Opts) { | 
|  | assert((Options.EmitNotes || Options.EmitData) && | 
|  | "GCOVProfiler asked to do nothing?"); | 
|  | ReversedVersion[0] = Options.Version[3]; | 
|  | ReversedVersion[1] = Options.Version[2]; | 
|  | ReversedVersion[2] = Options.Version[1]; | 
|  | ReversedVersion[3] = Options.Version[0]; | 
|  | ReversedVersion[4] = '\0'; | 
|  | } | 
|  | bool | 
|  | runOnModule(Module &M, | 
|  | std::function<const TargetLibraryInfo &(Function &F)> GetTLI); | 
|  |  | 
|  | private: | 
|  | // Create the .gcno files for the Module based on DebugInfo. | 
|  | void emitProfileNotes(); | 
|  |  | 
|  | // Modify the program to track transitions along edges and call into the | 
|  | // profiling runtime to emit .gcda files when run. | 
|  | bool emitProfileArcs(); | 
|  |  | 
|  | bool isFunctionInstrumented(const Function &F); | 
|  | std::vector<Regex> createRegexesFromString(StringRef RegexesStr); | 
|  | static bool doesFilenameMatchARegex(StringRef Filename, | 
|  | std::vector<Regex> &Regexes); | 
|  |  | 
|  | // Get pointers to the functions in the runtime library. | 
|  | FunctionCallee getStartFileFunc(const TargetLibraryInfo *TLI); | 
|  | FunctionCallee getEmitFunctionFunc(const TargetLibraryInfo *TLI); | 
|  | FunctionCallee getEmitArcsFunc(const TargetLibraryInfo *TLI); | 
|  | FunctionCallee getSummaryInfoFunc(); | 
|  | FunctionCallee getEndFileFunc(); | 
|  |  | 
|  | // Add the function to write out all our counters to the global destructor | 
|  | // list. | 
|  | Function * | 
|  | insertCounterWriteout(ArrayRef<std::pair<GlobalVariable *, MDNode *>>); | 
|  | Function *insertFlush(ArrayRef<std::pair<GlobalVariable *, MDNode *>>); | 
|  |  | 
|  | void AddFlushBeforeForkAndExec(); | 
|  |  | 
|  | enum class GCovFileType { GCNO, GCDA }; | 
|  | std::string mangleName(const DICompileUnit *CU, GCovFileType FileType); | 
|  |  | 
|  | GCOVOptions Options; | 
|  |  | 
|  | // Reversed, NUL-terminated copy of Options.Version. | 
|  | char ReversedVersion[5]; | 
|  | // Checksum, produced by hash of EdgeDestinations | 
|  | SmallVector<uint32_t, 4> FileChecksums; | 
|  |  | 
|  | Module *M = nullptr; | 
|  | std::function<const TargetLibraryInfo &(Function &F)> GetTLI; | 
|  | LLVMContext *Ctx = nullptr; | 
|  | SmallVector<std::unique_ptr<GCOVFunction>, 16> Funcs; | 
|  | std::vector<Regex> FilterRe; | 
|  | std::vector<Regex> ExcludeRe; | 
|  | StringMap<bool> InstrumentedFiles; | 
|  | }; | 
|  |  | 
|  | class GCOVProfilerLegacyPass : public ModulePass { | 
|  | public: | 
|  | static char ID; | 
|  | GCOVProfilerLegacyPass() | 
|  | : GCOVProfilerLegacyPass(GCOVOptions::getDefault()) {} | 
|  | GCOVProfilerLegacyPass(const GCOVOptions &Opts) | 
|  | : ModulePass(ID), Profiler(Opts) { | 
|  | initializeGCOVProfilerLegacyPassPass(*PassRegistry::getPassRegistry()); | 
|  | } | 
|  | StringRef getPassName() const override { return "GCOV Profiler"; } | 
|  |  | 
|  | bool runOnModule(Module &M) override { | 
|  | return Profiler.runOnModule(M, [this](Function &F) -> TargetLibraryInfo & { | 
|  | return getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void getAnalysisUsage(AnalysisUsage &AU) const override { | 
|  | AU.addRequired<TargetLibraryInfoWrapperPass>(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | GCOVProfiler Profiler; | 
|  | }; | 
|  | } | 
|  |  | 
|  | char GCOVProfilerLegacyPass::ID = 0; | 
|  | INITIALIZE_PASS_BEGIN( | 
|  | GCOVProfilerLegacyPass, "insert-gcov-profiling", | 
|  | "Insert instrumentation for GCOV profiling", false, false) | 
|  | INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) | 
|  | INITIALIZE_PASS_END( | 
|  | GCOVProfilerLegacyPass, "insert-gcov-profiling", | 
|  | "Insert instrumentation for GCOV profiling", false, false) | 
|  |  | 
|  | ModulePass *llvm::createGCOVProfilerPass(const GCOVOptions &Options) { | 
|  | return new GCOVProfilerLegacyPass(Options); | 
|  | } | 
|  |  | 
|  | static StringRef getFunctionName(const DISubprogram *SP) { | 
|  | if (!SP->getLinkageName().empty()) | 
|  | return SP->getLinkageName(); | 
|  | return SP->getName(); | 
|  | } | 
|  |  | 
|  | /// Extract a filename for a DISubprogram. | 
|  | /// | 
|  | /// Prefer relative paths in the coverage notes. Clang also may split | 
|  | /// up absolute paths into a directory and filename component. When | 
|  | /// the relative path doesn't exist, reconstruct the absolute path. | 
|  | static SmallString<128> getFilename(const DISubprogram *SP) { | 
|  | SmallString<128> Path; | 
|  | StringRef RelPath = SP->getFilename(); | 
|  | if (sys::fs::exists(RelPath)) | 
|  | Path = RelPath; | 
|  | else | 
|  | sys::path::append(Path, SP->getDirectory(), SP->getFilename()); | 
|  | return Path; | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | class GCOVRecord { | 
|  | protected: | 
|  | static const char *const LinesTag; | 
|  | static const char *const FunctionTag; | 
|  | static const char *const BlockTag; | 
|  | static const char *const EdgeTag; | 
|  |  | 
|  | GCOVRecord() = default; | 
|  |  | 
|  | void writeBytes(const char *Bytes, int Size) { | 
|  | os->write(Bytes, 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 | 
|  | static 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() / 4) + 1; | 
|  | } | 
|  |  | 
|  | 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)(4 - (s.size() % 4)) > 0); | 
|  | assert((unsigned)(4 - (s.size() % 4)) <= 4); | 
|  | writeBytes("\0\0\0\0", 4 - (s.size() % 4)); | 
|  | } | 
|  |  | 
|  | raw_ostream *os; | 
|  | }; | 
|  | const char *const GCOVRecord::LinesTag = "\0\0\x45\x01"; | 
|  | const char *const GCOVRecord::FunctionTag = "\0\0\0\1"; | 
|  | const char *const GCOVRecord::BlockTag = "\0\0\x41\x01"; | 
|  | const char *const GCOVRecord::EdgeTag = "\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) { | 
|  | assert(Line != 0 && "Line zero is not a valid real line number."); | 
|  | Lines.push_back(Line); | 
|  | } | 
|  |  | 
|  | uint32_t length() const { | 
|  | // Here 2 = 1 for string length + 1 for '0' id#. | 
|  | return lengthOfGCOVString(Filename) + 2 + Lines.size(); | 
|  | } | 
|  |  | 
|  | void writeOut() { | 
|  | write(0); | 
|  | writeGCOVString(Filename); | 
|  | for (int i = 0, e = Lines.size(); i != e; ++i) | 
|  | write(Lines[i]); | 
|  | } | 
|  |  | 
|  | GCOVLines(StringRef F, raw_ostream *os) | 
|  | : Filename(F) { | 
|  | this->os = os; | 
|  | } | 
|  |  | 
|  | private: | 
|  | 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(StringRef Filename) { | 
|  | return LinesByFile.try_emplace(Filename, Filename, os).first->second; | 
|  | } | 
|  |  | 
|  | void addEdge(GCOVBlock &Successor) { | 
|  | OutEdges.push_back(&Successor); | 
|  | } | 
|  |  | 
|  | void writeOut() { | 
|  | uint32_t Len = 3; | 
|  | SmallVector<StringMapEntry<GCOVLines> *, 32> SortedLinesByFile; | 
|  | for (auto &I : LinesByFile) { | 
|  | Len += I.second.length(); | 
|  | SortedLinesByFile.push_back(&I); | 
|  | } | 
|  |  | 
|  | writeBytes(LinesTag, 4); | 
|  | write(Len); | 
|  | write(Number); | 
|  |  | 
|  | llvm::sort(SortedLinesByFile, [](StringMapEntry<GCOVLines> *LHS, | 
|  | StringMapEntry<GCOVLines> *RHS) { | 
|  | return LHS->getKey() < RHS->getKey(); | 
|  | }); | 
|  | for (auto &I : SortedLinesByFile) | 
|  | I->getValue().writeOut(); | 
|  | write(0); | 
|  | write(0); | 
|  | } | 
|  |  | 
|  | GCOVBlock(const GCOVBlock &RHS) : GCOVRecord(RHS), Number(RHS.Number) { | 
|  | // Only allow copy before edges and lines have been added. After that, | 
|  | // there are inter-block pointers (eg: edges) that won't take kindly to | 
|  | // blocks being copied or moved around. | 
|  | assert(LinesByFile.empty()); | 
|  | assert(OutEdges.empty()); | 
|  | } | 
|  |  | 
|  | private: | 
|  | friend class GCOVFunction; | 
|  |  | 
|  | GCOVBlock(uint32_t Number, raw_ostream *os) | 
|  | : Number(Number) { | 
|  | this->os = os; | 
|  | } | 
|  |  | 
|  | uint32_t Number; | 
|  | StringMap<GCOVLines> LinesByFile; | 
|  | SmallVector<GCOVBlock *, 4> OutEdges; | 
|  | }; | 
|  |  | 
|  | // 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(const DISubprogram *SP, Function *F, raw_ostream *os, | 
|  | uint32_t Ident, bool UseCfgChecksum, bool ExitBlockBeforeBody) | 
|  | : SP(SP), Ident(Ident), UseCfgChecksum(UseCfgChecksum), CfgChecksum(0), | 
|  | ReturnBlock(1, os) { | 
|  | this->os = os; | 
|  |  | 
|  | LLVM_DEBUG(dbgs() << "Function: " << getFunctionName(SP) << "\n"); | 
|  |  | 
|  | uint32_t i = 0; | 
|  | for (auto &BB : *F) { | 
|  | // Skip index 1 if it's assigned to the ReturnBlock. | 
|  | if (i == 1 && ExitBlockBeforeBody) | 
|  | ++i; | 
|  | Blocks.insert(std::make_pair(&BB, GCOVBlock(i++, os))); | 
|  | } | 
|  | if (!ExitBlockBeforeBody) | 
|  | ReturnBlock.Number = i; | 
|  |  | 
|  | std::string FunctionNameAndLine; | 
|  | raw_string_ostream FNLOS(FunctionNameAndLine); | 
|  | FNLOS << getFunctionName(SP) << SP->getLine(); | 
|  | FNLOS.flush(); | 
|  | FuncChecksum = hash_value(FunctionNameAndLine); | 
|  | } | 
|  |  | 
|  | GCOVBlock &getBlock(BasicBlock *BB) { | 
|  | return Blocks.find(BB)->second; | 
|  | } | 
|  |  | 
|  | GCOVBlock &getReturnBlock() { | 
|  | return ReturnBlock; | 
|  | } | 
|  |  | 
|  | std::string getEdgeDestinations() { | 
|  | std::string EdgeDestinations; | 
|  | raw_string_ostream EDOS(EdgeDestinations); | 
|  | Function *F = Blocks.begin()->first->getParent(); | 
|  | for (BasicBlock &I : *F) { | 
|  | GCOVBlock &Block = getBlock(&I); | 
|  | for (int i = 0, e = Block.OutEdges.size(); i != e; ++i) | 
|  | EDOS << Block.OutEdges[i]->Number; | 
|  | } | 
|  | return EdgeDestinations; | 
|  | } | 
|  |  | 
|  | uint32_t getFuncChecksum() const { | 
|  | return FuncChecksum; | 
|  | } | 
|  |  | 
|  | void setCfgChecksum(uint32_t Checksum) { | 
|  | CfgChecksum = Checksum; | 
|  | } | 
|  |  | 
|  | void writeOut() { | 
|  | writeBytes(FunctionTag, 4); | 
|  | SmallString<128> Filename = getFilename(SP); | 
|  | uint32_t BlockLen = 1 + 1 + 1 + lengthOfGCOVString(getFunctionName(SP)) + | 
|  | 1 + lengthOfGCOVString(Filename) + 1; | 
|  | if (UseCfgChecksum) | 
|  | ++BlockLen; | 
|  | write(BlockLen); | 
|  | write(Ident); | 
|  | write(FuncChecksum); | 
|  | if (UseCfgChecksum) | 
|  | write(CfgChecksum); | 
|  | writeGCOVString(getFunctionName(SP)); | 
|  | writeGCOVString(Filename); | 
|  | write(SP->getLine()); | 
|  |  | 
|  | // Emit count of blocks. | 
|  | writeBytes(BlockTag, 4); | 
|  | write(Blocks.size() + 1); | 
|  | for (int i = 0, e = Blocks.size() + 1; i != e; ++i) { | 
|  | write(0);  // No flags on our blocks. | 
|  | } | 
|  | LLVM_DEBUG(dbgs() << Blocks.size() << " blocks.\n"); | 
|  |  | 
|  | // Emit edges between blocks. | 
|  | if (Blocks.empty()) return; | 
|  | Function *F = Blocks.begin()->first->getParent(); | 
|  | for (BasicBlock &I : *F) { | 
|  | GCOVBlock &Block = getBlock(&I); | 
|  | if (Block.OutEdges.empty()) continue; | 
|  |  | 
|  | writeBytes(EdgeTag, 4); | 
|  | write(Block.OutEdges.size() * 2 + 1); | 
|  | write(Block.Number); | 
|  | for (int i = 0, e = Block.OutEdges.size(); i != e; ++i) { | 
|  | LLVM_DEBUG(dbgs() << Block.Number << " -> " | 
|  | << Block.OutEdges[i]->Number << "\n"); | 
|  | write(Block.OutEdges[i]->Number); | 
|  | write(0);  // no flags | 
|  | } | 
|  | } | 
|  |  | 
|  | // Emit lines for each block. | 
|  | for (BasicBlock &I : *F) | 
|  | getBlock(&I).writeOut(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | const DISubprogram *SP; | 
|  | uint32_t Ident; | 
|  | uint32_t FuncChecksum; | 
|  | bool UseCfgChecksum; | 
|  | uint32_t CfgChecksum; | 
|  | DenseMap<BasicBlock *, GCOVBlock> Blocks; | 
|  | GCOVBlock ReturnBlock; | 
|  | }; | 
|  | } | 
|  |  | 
|  | // RegexesStr is a string containing differents regex separated by a semi-colon. | 
|  | // For example "foo\..*$;bar\..*$". | 
|  | std::vector<Regex> GCOVProfiler::createRegexesFromString(StringRef RegexesStr) { | 
|  | std::vector<Regex> Regexes; | 
|  | while (!RegexesStr.empty()) { | 
|  | std::pair<StringRef, StringRef> HeadTail = RegexesStr.split(';'); | 
|  | if (!HeadTail.first.empty()) { | 
|  | Regex Re(HeadTail.first); | 
|  | std::string Err; | 
|  | if (!Re.isValid(Err)) { | 
|  | Ctx->emitError(Twine("Regex ") + HeadTail.first + | 
|  | " is not valid: " + Err); | 
|  | } | 
|  | Regexes.emplace_back(std::move(Re)); | 
|  | } | 
|  | RegexesStr = HeadTail.second; | 
|  | } | 
|  | return Regexes; | 
|  | } | 
|  |  | 
|  | bool GCOVProfiler::doesFilenameMatchARegex(StringRef Filename, | 
|  | std::vector<Regex> &Regexes) { | 
|  | for (Regex &Re : Regexes) { | 
|  | if (Re.match(Filename)) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool GCOVProfiler::isFunctionInstrumented(const Function &F) { | 
|  | if (FilterRe.empty() && ExcludeRe.empty()) { | 
|  | return true; | 
|  | } | 
|  | SmallString<128> Filename = getFilename(F.getSubprogram()); | 
|  | auto It = InstrumentedFiles.find(Filename); | 
|  | if (It != InstrumentedFiles.end()) { | 
|  | return It->second; | 
|  | } | 
|  |  | 
|  | SmallString<256> RealPath; | 
|  | StringRef RealFilename; | 
|  |  | 
|  | // Path can be | 
|  | // /usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/*.h so for | 
|  | // such a case we must get the real_path. | 
|  | if (sys::fs::real_path(Filename, RealPath)) { | 
|  | // real_path can fail with path like "foo.c". | 
|  | RealFilename = Filename; | 
|  | } else { | 
|  | RealFilename = RealPath; | 
|  | } | 
|  |  | 
|  | bool ShouldInstrument; | 
|  | if (FilterRe.empty()) { | 
|  | ShouldInstrument = !doesFilenameMatchARegex(RealFilename, ExcludeRe); | 
|  | } else if (ExcludeRe.empty()) { | 
|  | ShouldInstrument = doesFilenameMatchARegex(RealFilename, FilterRe); | 
|  | } else { | 
|  | ShouldInstrument = doesFilenameMatchARegex(RealFilename, FilterRe) && | 
|  | !doesFilenameMatchARegex(RealFilename, ExcludeRe); | 
|  | } | 
|  | InstrumentedFiles[Filename] = ShouldInstrument; | 
|  | return ShouldInstrument; | 
|  | } | 
|  |  | 
|  | std::string GCOVProfiler::mangleName(const DICompileUnit *CU, | 
|  | GCovFileType OutputType) { | 
|  | bool Notes = OutputType == GCovFileType::GCNO; | 
|  |  | 
|  | if (NamedMDNode *GCov = M->getNamedMetadata("llvm.gcov")) { | 
|  | for (int i = 0, e = GCov->getNumOperands(); i != e; ++i) { | 
|  | MDNode *N = GCov->getOperand(i); | 
|  | bool ThreeElement = N->getNumOperands() == 3; | 
|  | if (!ThreeElement && N->getNumOperands() != 2) | 
|  | continue; | 
|  | if (dyn_cast<MDNode>(N->getOperand(ThreeElement ? 2 : 1)) != CU) | 
|  | continue; | 
|  |  | 
|  | if (ThreeElement) { | 
|  | // These nodes have no mangling to apply, it's stored mangled in the | 
|  | // bitcode. | 
|  | MDString *NotesFile = dyn_cast<MDString>(N->getOperand(0)); | 
|  | MDString *DataFile = dyn_cast<MDString>(N->getOperand(1)); | 
|  | if (!NotesFile || !DataFile) | 
|  | continue; | 
|  | return Notes ? NotesFile->getString() : DataFile->getString(); | 
|  | } | 
|  |  | 
|  | MDString *GCovFile = dyn_cast<MDString>(N->getOperand(0)); | 
|  | if (!GCovFile) | 
|  | continue; | 
|  |  | 
|  | SmallString<128> Filename = GCovFile->getString(); | 
|  | sys::path::replace_extension(Filename, Notes ? "gcno" : "gcda"); | 
|  | return Filename.str(); | 
|  | } | 
|  | } | 
|  |  | 
|  | SmallString<128> Filename = CU->getFilename(); | 
|  | sys::path::replace_extension(Filename, Notes ? "gcno" : "gcda"); | 
|  | StringRef FName = sys::path::filename(Filename); | 
|  | SmallString<128> CurPath; | 
|  | if (sys::fs::current_path(CurPath)) return FName; | 
|  | sys::path::append(CurPath, FName); | 
|  | return CurPath.str(); | 
|  | } | 
|  |  | 
|  | bool GCOVProfiler::runOnModule( | 
|  | Module &M, std::function<const TargetLibraryInfo &(Function &F)> GetTLI) { | 
|  | this->M = &M; | 
|  | this->GetTLI = std::move(GetTLI); | 
|  | Ctx = &M.getContext(); | 
|  |  | 
|  | AddFlushBeforeForkAndExec(); | 
|  |  | 
|  | FilterRe = createRegexesFromString(Options.Filter); | 
|  | ExcludeRe = createRegexesFromString(Options.Exclude); | 
|  |  | 
|  | if (Options.EmitNotes) emitProfileNotes(); | 
|  | if (Options.EmitData) return emitProfileArcs(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | PreservedAnalyses GCOVProfilerPass::run(Module &M, | 
|  | ModuleAnalysisManager &AM) { | 
|  |  | 
|  | GCOVProfiler Profiler(GCOVOpts); | 
|  | FunctionAnalysisManager &FAM = | 
|  | AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager(); | 
|  |  | 
|  | if (!Profiler.runOnModule(M, [&](Function &F) -> TargetLibraryInfo & { | 
|  | return FAM.getResult<TargetLibraryAnalysis>(F); | 
|  | })) | 
|  | return PreservedAnalyses::all(); | 
|  |  | 
|  | return PreservedAnalyses::none(); | 
|  | } | 
|  |  | 
|  | static bool functionHasLines(Function &F) { | 
|  | // Check whether this function actually has any source lines. Not only | 
|  | // do these waste space, they also can crash gcov. | 
|  | for (auto &BB : F) { | 
|  | for (auto &I : BB) { | 
|  | // Debug intrinsic locations correspond to the location of the | 
|  | // declaration, not necessarily any statements or expressions. | 
|  | if (isa<DbgInfoIntrinsic>(&I)) continue; | 
|  |  | 
|  | const DebugLoc &Loc = I.getDebugLoc(); | 
|  | if (!Loc) | 
|  | continue; | 
|  |  | 
|  | // Artificial lines such as calls to the global constructors. | 
|  | if (Loc.getLine() == 0) continue; | 
|  |  | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static bool isUsingScopeBasedEH(Function &F) { | 
|  | if (!F.hasPersonalityFn()) return false; | 
|  |  | 
|  | EHPersonality Personality = classifyEHPersonality(F.getPersonalityFn()); | 
|  | return isScopedEHPersonality(Personality); | 
|  | } | 
|  |  | 
|  | static bool shouldKeepInEntry(BasicBlock::iterator It) { | 
|  | if (isa<AllocaInst>(*It)) return true; | 
|  | if (isa<DbgInfoIntrinsic>(*It)) return true; | 
|  | if (auto *II = dyn_cast<IntrinsicInst>(It)) { | 
|  | if (II->getIntrinsicID() == llvm::Intrinsic::localescape) return true; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void GCOVProfiler::AddFlushBeforeForkAndExec() { | 
|  | SmallVector<Instruction *, 2> ForkAndExecs; | 
|  | for (auto &F : M->functions()) { | 
|  | auto *TLI = &GetTLI(F); | 
|  | for (auto &I : instructions(F)) { | 
|  | if (CallInst *CI = dyn_cast<CallInst>(&I)) { | 
|  | if (Function *Callee = CI->getCalledFunction()) { | 
|  | LibFunc LF; | 
|  | if (TLI->getLibFunc(*Callee, LF) && | 
|  | (LF == LibFunc_fork || LF == LibFunc_execl || | 
|  | LF == LibFunc_execle || LF == LibFunc_execlp || | 
|  | LF == LibFunc_execv || LF == LibFunc_execvp || | 
|  | LF == LibFunc_execve || LF == LibFunc_execvpe || | 
|  | LF == LibFunc_execvP)) { | 
|  | ForkAndExecs.push_back(&I); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // We need to split the block after the fork/exec call | 
|  | // because else the counters for the lines after will be | 
|  | // the same as before the call. | 
|  | for (auto I : ForkAndExecs) { | 
|  | IRBuilder<> Builder(I); | 
|  | FunctionType *FTy = FunctionType::get(Builder.getVoidTy(), {}, false); | 
|  | FunctionCallee GCOVFlush = M->getOrInsertFunction("__gcov_flush", FTy); | 
|  | Builder.CreateCall(GCOVFlush); | 
|  | I->getParent()->splitBasicBlock(I); | 
|  | } | 
|  | } | 
|  |  | 
|  | void GCOVProfiler::emitProfileNotes() { | 
|  | NamedMDNode *CU_Nodes = M->getNamedMetadata("llvm.dbg.cu"); | 
|  | if (!CU_Nodes) return; | 
|  |  | 
|  | for (unsigned i = 0, e = CU_Nodes->getNumOperands(); 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. | 
|  |  | 
|  | auto *CU = cast<DICompileUnit>(CU_Nodes->getOperand(i)); | 
|  |  | 
|  | // Skip module skeleton (and module) CUs. | 
|  | if (CU->getDWOId()) | 
|  | continue; | 
|  |  | 
|  | std::error_code EC; | 
|  | raw_fd_ostream out(mangleName(CU, GCovFileType::GCNO), EC, | 
|  | sys::fs::OF_None); | 
|  | if (EC) { | 
|  | Ctx->emitError(Twine("failed to open coverage notes file for writing: ") + | 
|  | EC.message()); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | std::string EdgeDestinations; | 
|  |  | 
|  | unsigned FunctionIdent = 0; | 
|  | for (auto &F : M->functions()) { | 
|  | DISubprogram *SP = F.getSubprogram(); | 
|  | if (!SP) continue; | 
|  | if (!functionHasLines(F) || !isFunctionInstrumented(F)) | 
|  | continue; | 
|  | // TODO: Functions using scope-based EH are currently not supported. | 
|  | if (isUsingScopeBasedEH(F)) continue; | 
|  |  | 
|  | // gcov expects every function to start with an entry block that has a | 
|  | // single successor, so split the entry block to make sure of that. | 
|  | BasicBlock &EntryBlock = F.getEntryBlock(); | 
|  | BasicBlock::iterator It = EntryBlock.begin(); | 
|  | while (shouldKeepInEntry(It)) | 
|  | ++It; | 
|  | EntryBlock.splitBasicBlock(It); | 
|  |  | 
|  | Funcs.push_back(std::make_unique<GCOVFunction>(SP, &F, &out, FunctionIdent++, | 
|  | Options.UseCfgChecksum, | 
|  | Options.ExitBlockBeforeBody)); | 
|  | GCOVFunction &Func = *Funcs.back(); | 
|  |  | 
|  | // Add the function line number to the lines of the entry block | 
|  | // to have a counter for the function definition. | 
|  | uint32_t Line = SP->getLine(); | 
|  | auto Filename = getFilename(SP); | 
|  |  | 
|  | // Artificial functions such as global initializers | 
|  | if (!SP->isArtificial()) | 
|  | Func.getBlock(&EntryBlock).getFile(Filename).addLine(Line); | 
|  |  | 
|  | for (auto &BB : F) { | 
|  | GCOVBlock &Block = Func.getBlock(&BB); | 
|  | Instruction *TI = BB.getTerminator(); | 
|  | if (int successors = TI->getNumSuccessors()) { | 
|  | for (int i = 0; i != successors; ++i) { | 
|  | Block.addEdge(Func.getBlock(TI->getSuccessor(i))); | 
|  | } | 
|  | } else if (isa<ReturnInst>(TI)) { | 
|  | Block.addEdge(Func.getReturnBlock()); | 
|  | } | 
|  |  | 
|  | for (auto &I : BB) { | 
|  | // Debug intrinsic locations correspond to the location of the | 
|  | // declaration, not necessarily any statements or expressions. | 
|  | if (isa<DbgInfoIntrinsic>(&I)) continue; | 
|  |  | 
|  | const DebugLoc &Loc = I.getDebugLoc(); | 
|  | if (!Loc) | 
|  | continue; | 
|  |  | 
|  | // Artificial lines such as calls to the global constructors. | 
|  | if (Loc.getLine() == 0 || Loc.isImplicitCode()) | 
|  | continue; | 
|  |  | 
|  | if (Line == Loc.getLine()) continue; | 
|  | Line = Loc.getLine(); | 
|  | if (SP != getDISubprogram(Loc.getScope())) | 
|  | continue; | 
|  |  | 
|  | GCOVLines &Lines = Block.getFile(Filename); | 
|  | Lines.addLine(Loc.getLine()); | 
|  | } | 
|  | Line = 0; | 
|  | } | 
|  | EdgeDestinations += Func.getEdgeDestinations(); | 
|  | } | 
|  |  | 
|  | FileChecksums.push_back(hash_value(EdgeDestinations)); | 
|  | out.write("oncg", 4); | 
|  | out.write(ReversedVersion, 4); | 
|  | out.write(reinterpret_cast<char*>(&FileChecksums.back()), 4); | 
|  |  | 
|  | for (auto &Func : Funcs) { | 
|  | Func->setCfgChecksum(FileChecksums.back()); | 
|  | Func->writeOut(); | 
|  | } | 
|  |  | 
|  | out.write("\0\0\0\0\0\0\0\0", 8);  // EOF | 
|  | out.close(); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool GCOVProfiler::emitProfileArcs() { | 
|  | NamedMDNode *CU_Nodes = M->getNamedMetadata("llvm.dbg.cu"); | 
|  | if (!CU_Nodes) return false; | 
|  |  | 
|  | bool Result = false; | 
|  | for (unsigned i = 0, e = CU_Nodes->getNumOperands(); i != e; ++i) { | 
|  | SmallVector<std::pair<GlobalVariable *, MDNode *>, 8> CountersBySP; | 
|  | for (auto &F : M->functions()) { | 
|  | DISubprogram *SP = F.getSubprogram(); | 
|  | if (!SP) continue; | 
|  | if (!functionHasLines(F) || !isFunctionInstrumented(F)) | 
|  | continue; | 
|  | // TODO: Functions using scope-based EH are currently not supported. | 
|  | if (isUsingScopeBasedEH(F)) continue; | 
|  | if (!Result) Result = true; | 
|  |  | 
|  | DenseMap<std::pair<BasicBlock *, BasicBlock *>, unsigned> EdgeToCounter; | 
|  | unsigned Edges = 0; | 
|  | for (auto &BB : F) { | 
|  | Instruction *TI = BB.getTerminator(); | 
|  | if (isa<ReturnInst>(TI)) { | 
|  | EdgeToCounter[{&BB, nullptr}] = Edges++; | 
|  | } else { | 
|  | for (BasicBlock *Succ : successors(TI)) { | 
|  | EdgeToCounter[{&BB, Succ}] = Edges++; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | ArrayType *CounterTy = | 
|  | ArrayType::get(Type::getInt64Ty(*Ctx), Edges); | 
|  | GlobalVariable *Counters = | 
|  | new GlobalVariable(*M, CounterTy, false, | 
|  | GlobalValue::InternalLinkage, | 
|  | Constant::getNullValue(CounterTy), | 
|  | "__llvm_gcov_ctr"); | 
|  | CountersBySP.push_back(std::make_pair(Counters, SP)); | 
|  |  | 
|  | // If a BB has several predecessors, use a PHINode to select | 
|  | // the correct counter. | 
|  | for (auto &BB : F) { | 
|  | const unsigned EdgeCount = | 
|  | std::distance(pred_begin(&BB), pred_end(&BB)); | 
|  | if (EdgeCount) { | 
|  | // The phi node must be at the begin of the BB. | 
|  | IRBuilder<> BuilderForPhi(&*BB.begin()); | 
|  | Type *Int64PtrTy = Type::getInt64PtrTy(*Ctx); | 
|  | PHINode *Phi = BuilderForPhi.CreatePHI(Int64PtrTy, EdgeCount); | 
|  | for (BasicBlock *Pred : predecessors(&BB)) { | 
|  | auto It = EdgeToCounter.find({Pred, &BB}); | 
|  | assert(It != EdgeToCounter.end()); | 
|  | const unsigned Edge = It->second; | 
|  | Value *EdgeCounter = BuilderForPhi.CreateConstInBoundsGEP2_64( | 
|  | Counters->getValueType(), Counters, 0, Edge); | 
|  | Phi->addIncoming(EdgeCounter, Pred); | 
|  | } | 
|  |  | 
|  | // Skip phis, landingpads. | 
|  | IRBuilder<> Builder(&*BB.getFirstInsertionPt()); | 
|  | Value *Count = Builder.CreateLoad(Builder.getInt64Ty(), Phi); | 
|  | Count = Builder.CreateAdd(Count, Builder.getInt64(1)); | 
|  | Builder.CreateStore(Count, Phi); | 
|  |  | 
|  | Instruction *TI = BB.getTerminator(); | 
|  | if (isa<ReturnInst>(TI)) { | 
|  | auto It = EdgeToCounter.find({&BB, nullptr}); | 
|  | assert(It != EdgeToCounter.end()); | 
|  | const unsigned Edge = It->second; | 
|  | Value *Counter = Builder.CreateConstInBoundsGEP2_64( | 
|  | Counters->getValueType(), Counters, 0, Edge); | 
|  | Value *Count = Builder.CreateLoad(Builder.getInt64Ty(), Counter); | 
|  | Count = Builder.CreateAdd(Count, Builder.getInt64(1)); | 
|  | Builder.CreateStore(Count, Counter); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | Function *WriteoutF = insertCounterWriteout(CountersBySP); | 
|  | Function *FlushF = insertFlush(CountersBySP); | 
|  |  | 
|  | // Create a small bit of code that registers the "__llvm_gcov_writeout" to | 
|  | // be executed at exit and the "__llvm_gcov_flush" function to be executed | 
|  | // when "__gcov_flush" is called. | 
|  | FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), false); | 
|  | Function *F = Function::Create(FTy, GlobalValue::InternalLinkage, | 
|  | "__llvm_gcov_init", M); | 
|  | F->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); | 
|  | F->setLinkage(GlobalValue::InternalLinkage); | 
|  | F->addFnAttr(Attribute::NoInline); | 
|  | if (Options.NoRedZone) | 
|  | F->addFnAttr(Attribute::NoRedZone); | 
|  |  | 
|  | BasicBlock *BB = BasicBlock::Create(*Ctx, "entry", F); | 
|  | IRBuilder<> Builder(BB); | 
|  |  | 
|  | FTy = FunctionType::get(Type::getVoidTy(*Ctx), false); | 
|  | Type *Params[] = { | 
|  | PointerType::get(FTy, 0), | 
|  | PointerType::get(FTy, 0) | 
|  | }; | 
|  | FTy = FunctionType::get(Builder.getVoidTy(), Params, false); | 
|  |  | 
|  | // Initialize the environment and register the local writeout and flush | 
|  | // functions. | 
|  | FunctionCallee GCOVInit = M->getOrInsertFunction("llvm_gcov_init", FTy); | 
|  | Builder.CreateCall(GCOVInit, {WriteoutF, FlushF}); | 
|  | Builder.CreateRetVoid(); | 
|  |  | 
|  | appendToGlobalCtors(*M, F, 0); | 
|  | } | 
|  |  | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | FunctionCallee GCOVProfiler::getStartFileFunc(const TargetLibraryInfo *TLI) { | 
|  | Type *Args[] = { | 
|  | Type::getInt8PtrTy(*Ctx),  // const char *orig_filename | 
|  | Type::getInt8PtrTy(*Ctx),  // const char version[4] | 
|  | Type::getInt32Ty(*Ctx),    // uint32_t checksum | 
|  | }; | 
|  | FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), Args, false); | 
|  | AttributeList AL; | 
|  | if (auto AK = TLI->getExtAttrForI32Param(false)) | 
|  | AL = AL.addParamAttribute(*Ctx, 2, AK); | 
|  | FunctionCallee Res = M->getOrInsertFunction("llvm_gcda_start_file", FTy, AL); | 
|  | return Res; | 
|  | } | 
|  |  | 
|  | FunctionCallee GCOVProfiler::getEmitFunctionFunc(const TargetLibraryInfo *TLI) { | 
|  | Type *Args[] = { | 
|  | Type::getInt32Ty(*Ctx),    // uint32_t ident | 
|  | Type::getInt8PtrTy(*Ctx),  // const char *function_name | 
|  | Type::getInt32Ty(*Ctx),    // uint32_t func_checksum | 
|  | Type::getInt8Ty(*Ctx),     // uint8_t use_extra_checksum | 
|  | Type::getInt32Ty(*Ctx),    // uint32_t cfg_checksum | 
|  | }; | 
|  | FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), Args, false); | 
|  | AttributeList AL; | 
|  | if (auto AK = TLI->getExtAttrForI32Param(false)) { | 
|  | AL = AL.addParamAttribute(*Ctx, 0, AK); | 
|  | AL = AL.addParamAttribute(*Ctx, 2, AK); | 
|  | AL = AL.addParamAttribute(*Ctx, 3, AK); | 
|  | AL = AL.addParamAttribute(*Ctx, 4, AK); | 
|  | } | 
|  | return M->getOrInsertFunction("llvm_gcda_emit_function", FTy); | 
|  | } | 
|  |  | 
|  | FunctionCallee GCOVProfiler::getEmitArcsFunc(const TargetLibraryInfo *TLI) { | 
|  | Type *Args[] = { | 
|  | Type::getInt32Ty(*Ctx),     // uint32_t num_counters | 
|  | Type::getInt64PtrTy(*Ctx),  // uint64_t *counters | 
|  | }; | 
|  | FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), Args, false); | 
|  | AttributeList AL; | 
|  | if (auto AK = TLI->getExtAttrForI32Param(false)) | 
|  | AL = AL.addParamAttribute(*Ctx, 0, AK); | 
|  | return M->getOrInsertFunction("llvm_gcda_emit_arcs", FTy, AL); | 
|  | } | 
|  |  | 
|  | FunctionCallee GCOVProfiler::getSummaryInfoFunc() { | 
|  | FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), false); | 
|  | return M->getOrInsertFunction("llvm_gcda_summary_info", FTy); | 
|  | } | 
|  |  | 
|  | FunctionCallee GCOVProfiler::getEndFileFunc() { | 
|  | FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), false); | 
|  | return M->getOrInsertFunction("llvm_gcda_end_file", FTy); | 
|  | } | 
|  |  | 
|  | Function *GCOVProfiler::insertCounterWriteout( | 
|  | ArrayRef<std::pair<GlobalVariable *, MDNode *> > CountersBySP) { | 
|  | FunctionType *WriteoutFTy = FunctionType::get(Type::getVoidTy(*Ctx), false); | 
|  | Function *WriteoutF = M->getFunction("__llvm_gcov_writeout"); | 
|  | if (!WriteoutF) | 
|  | WriteoutF = Function::Create(WriteoutFTy, GlobalValue::InternalLinkage, | 
|  | "__llvm_gcov_writeout", M); | 
|  | WriteoutF->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); | 
|  | WriteoutF->addFnAttr(Attribute::NoInline); | 
|  | if (Options.NoRedZone) | 
|  | WriteoutF->addFnAttr(Attribute::NoRedZone); | 
|  |  | 
|  | BasicBlock *BB = BasicBlock::Create(*Ctx, "entry", WriteoutF); | 
|  | IRBuilder<> Builder(BB); | 
|  |  | 
|  | auto *TLI = &GetTLI(*WriteoutF); | 
|  |  | 
|  | FunctionCallee StartFile = getStartFileFunc(TLI); | 
|  | FunctionCallee EmitFunction = getEmitFunctionFunc(TLI); | 
|  | FunctionCallee EmitArcs = getEmitArcsFunc(TLI); | 
|  | FunctionCallee SummaryInfo = getSummaryInfoFunc(); | 
|  | FunctionCallee EndFile = getEndFileFunc(); | 
|  |  | 
|  | NamedMDNode *CUNodes = M->getNamedMetadata("llvm.dbg.cu"); | 
|  | if (!CUNodes) { | 
|  | Builder.CreateRetVoid(); | 
|  | return WriteoutF; | 
|  | } | 
|  |  | 
|  | // Collect the relevant data into a large constant data structure that we can | 
|  | // walk to write out everything. | 
|  | StructType *StartFileCallArgsTy = StructType::create( | 
|  | {Builder.getInt8PtrTy(), Builder.getInt8PtrTy(), Builder.getInt32Ty()}); | 
|  | StructType *EmitFunctionCallArgsTy = StructType::create( | 
|  | {Builder.getInt32Ty(), Builder.getInt8PtrTy(), Builder.getInt32Ty(), | 
|  | Builder.getInt8Ty(), Builder.getInt32Ty()}); | 
|  | StructType *EmitArcsCallArgsTy = StructType::create( | 
|  | {Builder.getInt32Ty(), Builder.getInt64Ty()->getPointerTo()}); | 
|  | StructType *FileInfoTy = | 
|  | StructType::create({StartFileCallArgsTy, Builder.getInt32Ty(), | 
|  | EmitFunctionCallArgsTy->getPointerTo(), | 
|  | EmitArcsCallArgsTy->getPointerTo()}); | 
|  |  | 
|  | Constant *Zero32 = Builder.getInt32(0); | 
|  | // Build an explicit array of two zeros for use in ConstantExpr GEP building. | 
|  | Constant *TwoZero32s[] = {Zero32, Zero32}; | 
|  |  | 
|  | SmallVector<Constant *, 8> FileInfos; | 
|  | for (int i : llvm::seq<int>(0, CUNodes->getNumOperands())) { | 
|  | auto *CU = cast<DICompileUnit>(CUNodes->getOperand(i)); | 
|  |  | 
|  | // Skip module skeleton (and module) CUs. | 
|  | if (CU->getDWOId()) | 
|  | continue; | 
|  |  | 
|  | std::string FilenameGcda = mangleName(CU, GCovFileType::GCDA); | 
|  | uint32_t CfgChecksum = FileChecksums.empty() ? 0 : FileChecksums[i]; | 
|  | auto *StartFileCallArgs = ConstantStruct::get( | 
|  | StartFileCallArgsTy, {Builder.CreateGlobalStringPtr(FilenameGcda), | 
|  | Builder.CreateGlobalStringPtr(ReversedVersion), | 
|  | Builder.getInt32(CfgChecksum)}); | 
|  |  | 
|  | SmallVector<Constant *, 8> EmitFunctionCallArgsArray; | 
|  | SmallVector<Constant *, 8> EmitArcsCallArgsArray; | 
|  | for (int j : llvm::seq<int>(0, CountersBySP.size())) { | 
|  | auto *SP = cast_or_null<DISubprogram>(CountersBySP[j].second); | 
|  | uint32_t FuncChecksum = Funcs.empty() ? 0 : Funcs[j]->getFuncChecksum(); | 
|  | EmitFunctionCallArgsArray.push_back(ConstantStruct::get( | 
|  | EmitFunctionCallArgsTy, | 
|  | {Builder.getInt32(j), | 
|  | Options.FunctionNamesInData | 
|  | ? Builder.CreateGlobalStringPtr(getFunctionName(SP)) | 
|  | : Constant::getNullValue(Builder.getInt8PtrTy()), | 
|  | Builder.getInt32(FuncChecksum), | 
|  | Builder.getInt8(Options.UseCfgChecksum), | 
|  | Builder.getInt32(CfgChecksum)})); | 
|  |  | 
|  | GlobalVariable *GV = CountersBySP[j].first; | 
|  | unsigned Arcs = cast<ArrayType>(GV->getValueType())->getNumElements(); | 
|  | EmitArcsCallArgsArray.push_back(ConstantStruct::get( | 
|  | EmitArcsCallArgsTy, | 
|  | {Builder.getInt32(Arcs), ConstantExpr::getInBoundsGetElementPtr( | 
|  | GV->getValueType(), GV, TwoZero32s)})); | 
|  | } | 
|  | // Create global arrays for the two emit calls. | 
|  | int CountersSize = CountersBySP.size(); | 
|  | assert(CountersSize == (int)EmitFunctionCallArgsArray.size() && | 
|  | "Mismatched array size!"); | 
|  | assert(CountersSize == (int)EmitArcsCallArgsArray.size() && | 
|  | "Mismatched array size!"); | 
|  | auto *EmitFunctionCallArgsArrayTy = | 
|  | ArrayType::get(EmitFunctionCallArgsTy, CountersSize); | 
|  | auto *EmitFunctionCallArgsArrayGV = new GlobalVariable( | 
|  | *M, EmitFunctionCallArgsArrayTy, /*isConstant*/ true, | 
|  | GlobalValue::InternalLinkage, | 
|  | ConstantArray::get(EmitFunctionCallArgsArrayTy, | 
|  | EmitFunctionCallArgsArray), | 
|  | Twine("__llvm_internal_gcov_emit_function_args.") + Twine(i)); | 
|  | auto *EmitArcsCallArgsArrayTy = | 
|  | ArrayType::get(EmitArcsCallArgsTy, CountersSize); | 
|  | EmitFunctionCallArgsArrayGV->setUnnamedAddr( | 
|  | GlobalValue::UnnamedAddr::Global); | 
|  | auto *EmitArcsCallArgsArrayGV = new GlobalVariable( | 
|  | *M, EmitArcsCallArgsArrayTy, /*isConstant*/ true, | 
|  | GlobalValue::InternalLinkage, | 
|  | ConstantArray::get(EmitArcsCallArgsArrayTy, EmitArcsCallArgsArray), | 
|  | Twine("__llvm_internal_gcov_emit_arcs_args.") + Twine(i)); | 
|  | EmitArcsCallArgsArrayGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); | 
|  |  | 
|  | FileInfos.push_back(ConstantStruct::get( | 
|  | FileInfoTy, | 
|  | {StartFileCallArgs, Builder.getInt32(CountersSize), | 
|  | ConstantExpr::getInBoundsGetElementPtr(EmitFunctionCallArgsArrayTy, | 
|  | EmitFunctionCallArgsArrayGV, | 
|  | TwoZero32s), | 
|  | ConstantExpr::getInBoundsGetElementPtr( | 
|  | EmitArcsCallArgsArrayTy, EmitArcsCallArgsArrayGV, TwoZero32s)})); | 
|  | } | 
|  |  | 
|  | // If we didn't find anything to actually emit, bail on out. | 
|  | if (FileInfos.empty()) { | 
|  | Builder.CreateRetVoid(); | 
|  | return WriteoutF; | 
|  | } | 
|  |  | 
|  | // To simplify code, we cap the number of file infos we write out to fit | 
|  | // easily in a 32-bit signed integer. This gives consistent behavior between | 
|  | // 32-bit and 64-bit systems without requiring (potentially very slow) 64-bit | 
|  | // operations on 32-bit systems. It also seems unreasonable to try to handle | 
|  | // more than 2 billion files. | 
|  | if ((int64_t)FileInfos.size() > (int64_t)INT_MAX) | 
|  | FileInfos.resize(INT_MAX); | 
|  |  | 
|  | // Create a global for the entire data structure so we can walk it more | 
|  | // easily. | 
|  | auto *FileInfoArrayTy = ArrayType::get(FileInfoTy, FileInfos.size()); | 
|  | auto *FileInfoArrayGV = new GlobalVariable( | 
|  | *M, FileInfoArrayTy, /*isConstant*/ true, GlobalValue::InternalLinkage, | 
|  | ConstantArray::get(FileInfoArrayTy, FileInfos), | 
|  | "__llvm_internal_gcov_emit_file_info"); | 
|  | FileInfoArrayGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); | 
|  |  | 
|  | // Create the CFG for walking this data structure. | 
|  | auto *FileLoopHeader = | 
|  | BasicBlock::Create(*Ctx, "file.loop.header", WriteoutF); | 
|  | auto *CounterLoopHeader = | 
|  | BasicBlock::Create(*Ctx, "counter.loop.header", WriteoutF); | 
|  | auto *FileLoopLatch = BasicBlock::Create(*Ctx, "file.loop.latch", WriteoutF); | 
|  | auto *ExitBB = BasicBlock::Create(*Ctx, "exit", WriteoutF); | 
|  |  | 
|  | // We always have at least one file, so just branch to the header. | 
|  | Builder.CreateBr(FileLoopHeader); | 
|  |  | 
|  | // The index into the files structure is our loop induction variable. | 
|  | Builder.SetInsertPoint(FileLoopHeader); | 
|  | PHINode *IV = | 
|  | Builder.CreatePHI(Builder.getInt32Ty(), /*NumReservedValues*/ 2); | 
|  | IV->addIncoming(Builder.getInt32(0), BB); | 
|  | auto *FileInfoPtr = Builder.CreateInBoundsGEP( | 
|  | FileInfoArrayTy, FileInfoArrayGV, {Builder.getInt32(0), IV}); | 
|  | auto *StartFileCallArgsPtr = | 
|  | Builder.CreateStructGEP(FileInfoTy, FileInfoPtr, 0); | 
|  | auto *StartFileCall = Builder.CreateCall( | 
|  | StartFile, | 
|  | {Builder.CreateLoad(StartFileCallArgsTy->getElementType(0), | 
|  | Builder.CreateStructGEP(StartFileCallArgsTy, | 
|  | StartFileCallArgsPtr, 0)), | 
|  | Builder.CreateLoad(StartFileCallArgsTy->getElementType(1), | 
|  | Builder.CreateStructGEP(StartFileCallArgsTy, | 
|  | StartFileCallArgsPtr, 1)), | 
|  | Builder.CreateLoad(StartFileCallArgsTy->getElementType(2), | 
|  | Builder.CreateStructGEP(StartFileCallArgsTy, | 
|  | StartFileCallArgsPtr, 2))}); | 
|  | if (auto AK = TLI->getExtAttrForI32Param(false)) | 
|  | StartFileCall->addParamAttr(2, AK); | 
|  | auto *NumCounters = | 
|  | Builder.CreateLoad(FileInfoTy->getElementType(1), | 
|  | Builder.CreateStructGEP(FileInfoTy, FileInfoPtr, 1)); | 
|  | auto *EmitFunctionCallArgsArray = | 
|  | Builder.CreateLoad(FileInfoTy->getElementType(2), | 
|  | Builder.CreateStructGEP(FileInfoTy, FileInfoPtr, 2)); | 
|  | auto *EmitArcsCallArgsArray = | 
|  | Builder.CreateLoad(FileInfoTy->getElementType(3), | 
|  | Builder.CreateStructGEP(FileInfoTy, FileInfoPtr, 3)); | 
|  | auto *EnterCounterLoopCond = | 
|  | Builder.CreateICmpSLT(Builder.getInt32(0), NumCounters); | 
|  | Builder.CreateCondBr(EnterCounterLoopCond, CounterLoopHeader, FileLoopLatch); | 
|  |  | 
|  | Builder.SetInsertPoint(CounterLoopHeader); | 
|  | auto *JV = Builder.CreatePHI(Builder.getInt32Ty(), /*NumReservedValues*/ 2); | 
|  | JV->addIncoming(Builder.getInt32(0), FileLoopHeader); | 
|  | auto *EmitFunctionCallArgsPtr = Builder.CreateInBoundsGEP( | 
|  | EmitFunctionCallArgsTy, EmitFunctionCallArgsArray, JV); | 
|  | auto *EmitFunctionCall = Builder.CreateCall( | 
|  | EmitFunction, | 
|  | {Builder.CreateLoad(EmitFunctionCallArgsTy->getElementType(0), | 
|  | Builder.CreateStructGEP(EmitFunctionCallArgsTy, | 
|  | EmitFunctionCallArgsPtr, 0)), | 
|  | Builder.CreateLoad(EmitFunctionCallArgsTy->getElementType(1), | 
|  | Builder.CreateStructGEP(EmitFunctionCallArgsTy, | 
|  | EmitFunctionCallArgsPtr, 1)), | 
|  | Builder.CreateLoad(EmitFunctionCallArgsTy->getElementType(2), | 
|  | Builder.CreateStructGEP(EmitFunctionCallArgsTy, | 
|  | EmitFunctionCallArgsPtr, 2)), | 
|  | Builder.CreateLoad(EmitFunctionCallArgsTy->getElementType(3), | 
|  | Builder.CreateStructGEP(EmitFunctionCallArgsTy, | 
|  | EmitFunctionCallArgsPtr, 3)), | 
|  | Builder.CreateLoad(EmitFunctionCallArgsTy->getElementType(4), | 
|  | Builder.CreateStructGEP(EmitFunctionCallArgsTy, | 
|  | EmitFunctionCallArgsPtr, | 
|  | 4))}); | 
|  | if (auto AK = TLI->getExtAttrForI32Param(false)) { | 
|  | EmitFunctionCall->addParamAttr(0, AK); | 
|  | EmitFunctionCall->addParamAttr(2, AK); | 
|  | EmitFunctionCall->addParamAttr(3, AK); | 
|  | EmitFunctionCall->addParamAttr(4, AK); | 
|  | } | 
|  | auto *EmitArcsCallArgsPtr = | 
|  | Builder.CreateInBoundsGEP(EmitArcsCallArgsTy, EmitArcsCallArgsArray, JV); | 
|  | auto *EmitArcsCall = Builder.CreateCall( | 
|  | EmitArcs, | 
|  | {Builder.CreateLoad( | 
|  | EmitArcsCallArgsTy->getElementType(0), | 
|  | Builder.CreateStructGEP(EmitArcsCallArgsTy, EmitArcsCallArgsPtr, 0)), | 
|  | Builder.CreateLoad(EmitArcsCallArgsTy->getElementType(1), | 
|  | Builder.CreateStructGEP(EmitArcsCallArgsTy, | 
|  | EmitArcsCallArgsPtr, 1))}); | 
|  | if (auto AK = TLI->getExtAttrForI32Param(false)) | 
|  | EmitArcsCall->addParamAttr(0, AK); | 
|  | auto *NextJV = Builder.CreateAdd(JV, Builder.getInt32(1)); | 
|  | auto *CounterLoopCond = Builder.CreateICmpSLT(NextJV, NumCounters); | 
|  | Builder.CreateCondBr(CounterLoopCond, CounterLoopHeader, FileLoopLatch); | 
|  | JV->addIncoming(NextJV, CounterLoopHeader); | 
|  |  | 
|  | Builder.SetInsertPoint(FileLoopLatch); | 
|  | Builder.CreateCall(SummaryInfo, {}); | 
|  | Builder.CreateCall(EndFile, {}); | 
|  | auto *NextIV = Builder.CreateAdd(IV, Builder.getInt32(1)); | 
|  | auto *FileLoopCond = | 
|  | Builder.CreateICmpSLT(NextIV, Builder.getInt32(FileInfos.size())); | 
|  | Builder.CreateCondBr(FileLoopCond, FileLoopHeader, ExitBB); | 
|  | IV->addIncoming(NextIV, FileLoopLatch); | 
|  |  | 
|  | Builder.SetInsertPoint(ExitBB); | 
|  | Builder.CreateRetVoid(); | 
|  |  | 
|  | return WriteoutF; | 
|  | } | 
|  |  | 
|  | Function *GCOVProfiler:: | 
|  | insertFlush(ArrayRef<std::pair<GlobalVariable*, MDNode*> > CountersBySP) { | 
|  | FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), false); | 
|  | Function *FlushF = M->getFunction("__llvm_gcov_flush"); | 
|  | if (!FlushF) | 
|  | FlushF = Function::Create(FTy, GlobalValue::InternalLinkage, | 
|  | "__llvm_gcov_flush", M); | 
|  | else | 
|  | FlushF->setLinkage(GlobalValue::InternalLinkage); | 
|  | FlushF->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); | 
|  | FlushF->addFnAttr(Attribute::NoInline); | 
|  | if (Options.NoRedZone) | 
|  | FlushF->addFnAttr(Attribute::NoRedZone); | 
|  |  | 
|  | BasicBlock *Entry = BasicBlock::Create(*Ctx, "entry", FlushF); | 
|  |  | 
|  | // Write out the current counters. | 
|  | Function *WriteoutF = M->getFunction("__llvm_gcov_writeout"); | 
|  | assert(WriteoutF && "Need to create the writeout function first!"); | 
|  |  | 
|  | IRBuilder<> Builder(Entry); | 
|  | Builder.CreateCall(WriteoutF, {}); | 
|  |  | 
|  | // Zero out the counters. | 
|  | for (const auto &I : CountersBySP) { | 
|  | GlobalVariable *GV = I.first; | 
|  | Constant *Null = Constant::getNullValue(GV->getValueType()); | 
|  | Builder.CreateStore(Null, GV); | 
|  | } | 
|  |  | 
|  | Type *RetTy = FlushF->getReturnType(); | 
|  | if (RetTy == Type::getVoidTy(*Ctx)) | 
|  | Builder.CreateRetVoid(); | 
|  | else if (RetTy->isIntegerTy()) | 
|  | // Used if __llvm_gcov_flush was implicitly declared. | 
|  | Builder.CreateRet(ConstantInt::get(RetTy, 0)); | 
|  | else | 
|  | report_fatal_error("invalid return type for __llvm_gcov_flush"); | 
|  |  | 
|  | return FlushF; | 
|  | } |