LCOV - code coverage report
Current view: top level - lib/ProfileData - SampleProfWriter.cpp (source / functions) Hit Total Coverage
Test: llvm-toolchain.info Lines: 175 194 90.2 %
Date: 2018-10-20 13:21:21 Functions: 21 21 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //===- SampleProfWriter.cpp - Write LLVM sample profile data --------------===//
       2             : //
       3             : //                      The LLVM Compiler Infrastructure
       4             : //
       5             : // This file is distributed under the University of Illinois Open Source
       6             : // License. See LICENSE.TXT for details.
       7             : //
       8             : //===----------------------------------------------------------------------===//
       9             : //
      10             : // This file implements the class that writes LLVM sample profiles. It
      11             : // supports two file formats: text and binary. The textual representation
      12             : // is useful for debugging and testing purposes. The binary representation
      13             : // is more compact, resulting in smaller file sizes. However, they can
      14             : // both be used interchangeably.
      15             : //
      16             : // See lib/ProfileData/SampleProfReader.cpp for documentation on each of the
      17             : // supported formats.
      18             : //
      19             : //===----------------------------------------------------------------------===//
      20             : 
      21             : #include "llvm/ProfileData/SampleProfWriter.h"
      22             : #include "llvm/ADT/StringRef.h"
      23             : #include "llvm/ProfileData/ProfileCommon.h"
      24             : #include "llvm/ProfileData/SampleProf.h"
      25             : #include "llvm/Support/Endian.h"
      26             : #include "llvm/Support/EndianStream.h"
      27             : #include "llvm/Support/ErrorOr.h"
      28             : #include "llvm/Support/FileSystem.h"
      29             : #include "llvm/Support/LEB128.h"
      30             : #include "llvm/Support/MD5.h"
      31             : #include "llvm/Support/raw_ostream.h"
      32             : #include <algorithm>
      33             : #include <cstdint>
      34             : #include <memory>
      35             : #include <set>
      36             : #include <system_error>
      37             : #include <utility>
      38             : #include <vector>
      39             : 
      40             : using namespace llvm;
      41             : using namespace sampleprof;
      42             : 
      43             : std::error_code
      44          22 : SampleProfileWriter::write(const StringMap<FunctionSamples> &ProfileMap) {
      45          22 :   if (std::error_code EC = writeHeader(ProfileMap))
      46           0 :     return EC;
      47             : 
      48             :   // Sort the ProfileMap by total samples.
      49             :   typedef std::pair<StringRef, const FunctionSamples *> NameFunctionSamples;
      50             :   std::vector<NameFunctionSamples> V;
      51          88 :   for (const auto &I : ProfileMap)
      52          44 :     V.push_back(std::make_pair(I.getKey(), &I.second));
      53             : 
      54          22 :   std::stable_sort(
      55             :       V.begin(), V.end(),
      56             :       [](const NameFunctionSamples &A, const NameFunctionSamples &B) {
      57           0 :         if (A.second->getTotalSamples() == B.second->getTotalSamples())
      58           0 :           return A.first > B.first;
      59           0 :         return A.second->getTotalSamples() > B.second->getTotalSamples();
      60             :       });
      61             : 
      62          66 :   for (const auto &I : V) {
      63          44 :     if (std::error_code EC = write(*I.second))
      64           0 :       return EC;
      65             :   }
      66          22 :   return sampleprof_error::success;
      67             : }
      68             : 
      69           1 : std::error_code SampleProfileWriterCompactBinary::write(
      70             :     const StringMap<FunctionSamples> &ProfileMap) {
      71           1 :   if (std::error_code EC = SampleProfileWriter::write(ProfileMap))
      72           0 :     return EC;
      73           1 :   if (std::error_code EC = writeFuncOffsetTable())
      74           0 :     return EC;
      75           1 :   return sampleprof_error::success;
      76             : }
      77             : 
      78             : /// Write samples to a text file.
      79             : ///
      80             : /// Note: it may be tempting to implement this in terms of
      81             : /// FunctionSamples::print().  Please don't.  The dump functionality is intended
      82             : /// for debugging and has no specified form.
      83             : ///
      84             : /// The format used here is more structured and deliberate because
      85             : /// it needs to be parsed by the SampleProfileReaderText class.
      86          50 : std::error_code SampleProfileWriterText::write(const FunctionSamples &S) {
      87             :   auto &OS = *OutputStream;
      88          50 :   OS << S.getName() << ":" << S.getTotalSamples();
      89          50 :   if (Indent == 0)
      90          22 :     OS << ":" << S.getHeadSamples();
      91          50 :   OS << "\n";
      92             : 
      93          50 :   SampleSorter<LineLocation, SampleRecord> SortedSamples(S.getBodySamples());
      94         216 :   for (const auto &I : SortedSamples.get()) {
      95         166 :     LineLocation Loc = I->first;
      96             :     const SampleRecord &Sample = I->second;
      97         166 :     OS.indent(Indent + 1);
      98         166 :     if (Loc.Discriminator == 0)
      99         102 :       OS << Loc.LineOffset << ": ";
     100             :     else
     101         128 :       OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
     102             : 
     103         166 :     OS << Sample.getSamples();
     104             : 
     105         346 :     for (const auto &J : Sample.getCallTargets())
     106          14 :       OS << " " << J.first() << ":" << J.second;
     107         166 :     OS << "\n";
     108             :   }
     109             : 
     110             :   SampleSorter<LineLocation, FunctionSamplesMap> SortedCallsiteSamples(
     111          50 :       S.getCallsiteSamples());
     112          50 :   Indent += 1;
     113          76 :   for (const auto &I : SortedCallsiteSamples.get())
     114          54 :     for (const auto &FS : I->second) {
     115          28 :       LineLocation Loc = I->first;
     116          28 :       const FunctionSamples &CalleeSamples = FS.second;
     117          28 :       OS.indent(Indent);
     118          28 :       if (Loc.Discriminator == 0)
     119           4 :         OS << Loc.LineOffset << ": ";
     120             :       else
     121          48 :         OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
     122          28 :       if (std::error_code EC = write(CalleeSamples))
     123           0 :         return EC;
     124             :     }
     125          50 :   Indent -= 1;
     126             : 
     127          50 :   return sampleprof_error::success;
     128             : }
     129             : 
     130          48 : std::error_code SampleProfileWriterBinary::writeNameIdx(StringRef FName) {
     131          48 :   const auto &ret = NameTable.find(FName);
     132          48 :   if (ret == NameTable.end())
     133           0 :     return sampleprof_error::truncated_name_table;
     134          48 :   encodeULEB128(ret->second, *OutputStream);
     135          48 :   return sampleprof_error::success;
     136             : }
     137             : 
     138          46 : void SampleProfileWriterBinary::addName(StringRef FName) {
     139          92 :   NameTable.insert(std::make_pair(FName, 0));
     140          46 : }
     141             : 
     142          30 : void SampleProfileWriterBinary::addNames(const FunctionSamples &S) {
     143             :   // Add all the names in indirect call targets.
     144          96 :   for (const auto &I : S.getBodySamples()) {
     145             :     const SampleRecord &Sample = I.second;
     146         148 :     for (const auto &J : Sample.getCallTargets())
     147          16 :       addName(J.first());
     148             :   }
     149             : 
     150             :   // Recursively add all the names for inlined callsites.
     151          36 :   for (const auto &J : S.getCallsiteSamples())
     152          14 :     for (const auto &FS : J.second) {
     153           8 :       const FunctionSamples &CalleeSamples = FS.second;
     154           8 :       addName(CalleeSamples.getName());
     155           8 :       addNames(CalleeSamples);
     156             :     }
     157          30 : }
     158             : 
     159           9 : void SampleProfileWriterBinary::stablizeNameTable(std::set<StringRef> &V) {
     160             :   // Sort the names to make NameTable deterministic.
     161          43 :   for (const auto &I : NameTable)
     162          34 :     V.insert(I.first);
     163             :   int i = 0;
     164          43 :   for (const StringRef &N : V)
     165          34 :     NameTable[N] = i++;
     166           9 : }
     167             : 
     168           8 : std::error_code SampleProfileWriterRawBinary::writeNameTable() {
     169             :   auto &OS = *OutputStream;
     170             :   std::set<StringRef> V;
     171           8 :   stablizeNameTable(V);
     172             : 
     173             :   // Write out the name table.
     174           8 :   encodeULEB128(NameTable.size(), OS);
     175          38 :   for (auto N : V) {
     176          30 :     OS << N;
     177          30 :     encodeULEB128(0, OS);
     178             :   }
     179           8 :   return sampleprof_error::success;
     180             : }
     181             : 
     182           1 : std::error_code SampleProfileWriterCompactBinary::writeFuncOffsetTable() {
     183             :   auto &OS = *OutputStream;
     184             : 
     185             :   // Fill the slot remembered by TableOffset with the offset of FuncOffsetTable.
     186             :   auto &OFS = static_cast<raw_fd_ostream &>(OS);
     187             :   uint64_t FuncOffsetTableStart = OS.tell();
     188           1 :   if (OFS.seek(TableOffset) == (uint64_t)-1)
     189           0 :     return sampleprof_error::ostream_seek_unsupported;
     190             :   support::endian::Writer Writer(*OutputStream, support::little);
     191             :   Writer.write(FuncOffsetTableStart);
     192           1 :   if (OFS.seek(FuncOffsetTableStart) == (uint64_t)-1)
     193           0 :     return sampleprof_error::ostream_seek_unsupported;
     194             : 
     195             :   // Write out the table size.
     196           1 :   encodeULEB128(FuncOffsetTable.size(), OS);
     197             : 
     198             :   // Write out FuncOffsetTable.
     199           3 :   for (auto entry : FuncOffsetTable) {
     200           2 :     writeNameIdx(entry.first);
     201           2 :     encodeULEB128(entry.second, OS);
     202             :   }
     203           1 :   return sampleprof_error::success;
     204             : }
     205             : 
     206           1 : std::error_code SampleProfileWriterCompactBinary::writeNameTable() {
     207             :   auto &OS = *OutputStream;
     208             :   std::set<StringRef> V;
     209           1 :   stablizeNameTable(V);
     210             : 
     211             :   // Write out the name table.
     212           1 :   encodeULEB128(NameTable.size(), OS);
     213           5 :   for (auto N : V) {
     214           4 :     encodeULEB128(MD5Hash(N), OS);
     215             :   }
     216           1 :   return sampleprof_error::success;
     217             : }
     218             : 
     219           8 : std::error_code SampleProfileWriterRawBinary::writeMagicIdent() {
     220             :   auto &OS = *OutputStream;
     221             :   // Write file magic identifier.
     222           8 :   encodeULEB128(SPMagic(), OS);
     223           8 :   encodeULEB128(SPVersion(), OS);
     224           8 :   return sampleprof_error::success;
     225             : }
     226             : 
     227           1 : std::error_code SampleProfileWriterCompactBinary::writeMagicIdent() {
     228             :   auto &OS = *OutputStream;
     229             :   // Write file magic identifier.
     230           1 :   encodeULEB128(SPMagic(SPF_Compact_Binary), OS);
     231           1 :   encodeULEB128(SPVersion(), OS);
     232           1 :   return sampleprof_error::success;
     233             : }
     234             : 
     235           9 : std::error_code SampleProfileWriterBinary::writeHeader(
     236             :     const StringMap<FunctionSamples> &ProfileMap) {
     237           9 :   writeMagicIdent();
     238             : 
     239           9 :   computeSummary(ProfileMap);
     240           9 :   if (auto EC = writeSummary())
     241           0 :     return EC;
     242             : 
     243             :   // Generate the name table for all the functions referenced in the profile.
     244          40 :   for (const auto &I : ProfileMap) {
     245          22 :     addName(I.first());
     246          22 :     addNames(I.second);
     247             :   }
     248             : 
     249           9 :   writeNameTable();
     250           9 :   return sampleprof_error::success;
     251             : }
     252             : 
     253           1 : std::error_code SampleProfileWriterCompactBinary::writeHeader(
     254             :     const StringMap<FunctionSamples> &ProfileMap) {
     255             :   support::endian::Writer Writer(*OutputStream, support::little);
     256           1 :   if (auto EC = SampleProfileWriterBinary::writeHeader(ProfileMap))
     257           0 :     return EC;
     258             : 
     259             :   // Reserve a slot for the offset of function offset table. The slot will
     260             :   // be populated with the offset of FuncOffsetTable later.
     261           1 :   TableOffset = OutputStream->tell();
     262             :   Writer.write(static_cast<uint64_t>(-2));
     263           1 :   return sampleprof_error::success;
     264             : }
     265             : 
     266           9 : std::error_code SampleProfileWriterBinary::writeSummary() {
     267             :   auto &OS = *OutputStream;
     268           9 :   encodeULEB128(Summary->getTotalCount(), OS);
     269           9 :   encodeULEB128(Summary->getMaxCount(), OS);
     270           9 :   encodeULEB128(Summary->getMaxFunctionCount(), OS);
     271           9 :   encodeULEB128(Summary->getNumCounts(), OS);
     272           9 :   encodeULEB128(Summary->getNumFunctions(), OS);
     273             :   std::vector<ProfileSummaryEntry> &Entries = Summary->getDetailedSummary();
     274          18 :   encodeULEB128(Entries.size(), OS);
     275         153 :   for (auto Entry : Entries) {
     276         144 :     encodeULEB128(Entry.Cutoff, OS);
     277         144 :     encodeULEB128(Entry.MinCount, OS);
     278         144 :     encodeULEB128(Entry.NumCounts, OS);
     279             :   }
     280           9 :   return sampleprof_error::success;
     281             : }
     282          30 : std::error_code SampleProfileWriterBinary::writeBody(const FunctionSamples &S) {
     283             :   auto &OS = *OutputStream;
     284             : 
     285          30 :   if (std::error_code EC = writeNameIdx(S.getName()))
     286           0 :     return EC;
     287             : 
     288          30 :   encodeULEB128(S.getTotalSamples(), OS);
     289             : 
     290             :   // Emit all the body samples.
     291          30 :   encodeULEB128(S.getBodySamples().size(), OS);
     292          96 :   for (const auto &I : S.getBodySamples()) {
     293          66 :     LineLocation Loc = I.first;
     294             :     const SampleRecord &Sample = I.second;
     295          66 :     encodeULEB128(Loc.LineOffset, OS);
     296          66 :     encodeULEB128(Loc.Discriminator, OS);
     297          66 :     encodeULEB128(Sample.getSamples(), OS);
     298          66 :     encodeULEB128(Sample.getCallTargets().size(), OS);
     299         148 :     for (const auto &J : Sample.getCallTargets()) {
     300          16 :       StringRef Callee = J.first();
     301          16 :       uint64_t CalleeSamples = J.second;
     302          16 :       if (std::error_code EC = writeNameIdx(Callee))
     303           0 :         return EC;
     304          16 :       encodeULEB128(CalleeSamples, OS);
     305             :     }
     306             :   }
     307             : 
     308             :   // Recursively emit all the callsite samples.
     309             :   uint64_t NumCallsites = 0;
     310          36 :   for (const auto &J : S.getCallsiteSamples())
     311           6 :     NumCallsites += J.second.size();
     312          30 :   encodeULEB128(NumCallsites, OS);
     313          36 :   for (const auto &J : S.getCallsiteSamples())
     314          14 :     for (const auto &FS : J.second) {
     315           8 :       LineLocation Loc = J.first;
     316           8 :       const FunctionSamples &CalleeSamples = FS.second;
     317           8 :       encodeULEB128(Loc.LineOffset, OS);
     318           8 :       encodeULEB128(Loc.Discriminator, OS);
     319           8 :       if (std::error_code EC = writeBody(CalleeSamples))
     320           0 :         return EC;
     321             :     }
     322             : 
     323          30 :   return sampleprof_error::success;
     324             : }
     325             : 
     326             : /// Write samples of a top-level function to a binary file.
     327             : ///
     328             : /// \returns true if the samples were written successfully, false otherwise.
     329          20 : std::error_code SampleProfileWriterBinary::write(const FunctionSamples &S) {
     330          20 :   encodeULEB128(S.getHeadSamples(), *OutputStream);
     331          20 :   return writeBody(S);
     332             : }
     333             : 
     334             : std::error_code
     335           2 : SampleProfileWriterCompactBinary::write(const FunctionSamples &S) {
     336             :   uint64_t Offset = OutputStream->tell();
     337           2 :   StringRef Name = S.getName();
     338           2 :   FuncOffsetTable[Name] = Offset;
     339           2 :   encodeULEB128(S.getHeadSamples(), *OutputStream);
     340           2 :   return writeBody(S);
     341             : }
     342             : 
     343             : /// Create a sample profile file writer based on the specified format.
     344             : ///
     345             : /// \param Filename The file to create.
     346             : ///
     347             : /// \param Format Encoding format for the profile file.
     348             : ///
     349             : /// \returns an error code indicating the status of the created writer.
     350             : ErrorOr<std::unique_ptr<SampleProfileWriter>>
     351          17 : SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) {
     352             :   std::error_code EC;
     353          17 :   std::unique_ptr<raw_ostream> OS;
     354          17 :   if (Format == SPF_Binary || Format == SPF_Compact_Binary)
     355          12 :     OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::F_None));
     356             :   else
     357          22 :     OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::F_Text));
     358          17 :   if (EC)
     359           0 :     return EC;
     360             : 
     361          17 :   return create(OS, Format);
     362             : }
     363             : 
     364             : /// Create a sample profile stream writer based on the specified format.
     365             : ///
     366             : /// \param OS The output stream to store the profile data to.
     367             : ///
     368             : /// \param Format Encoding format for the profile file.
     369             : ///
     370             : /// \returns an error code indicating the status of the created writer.
     371             : ErrorOr<std::unique_ptr<SampleProfileWriter>>
     372          22 : SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
     373             :                             SampleProfileFormat Format) {
     374             :   std::error_code EC;
     375             :   std::unique_ptr<SampleProfileWriter> Writer;
     376             : 
     377          22 :   if (Format == SPF_Binary)
     378           8 :     Writer.reset(new SampleProfileWriterRawBinary(OS));
     379          14 :   else if (Format == SPF_Compact_Binary)
     380           1 :     Writer.reset(new SampleProfileWriterCompactBinary(OS));
     381          13 :   else if (Format == SPF_Text)
     382          13 :     Writer.reset(new SampleProfileWriterText(OS));
     383           0 :   else if (Format == SPF_GCC)
     384             :     EC = sampleprof_error::unsupported_writing_format;
     385             :   else
     386             :     EC = sampleprof_error::unrecognized_format;
     387             : 
     388          22 :   if (EC)
     389           0 :     return EC;
     390             : 
     391             :   return std::move(Writer);
     392             : }
     393             : 
     394           9 : void SampleProfileWriter::computeSummary(
     395             :     const StringMap<FunctionSamples> &ProfileMap) {
     396           9 :   SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
     397          40 :   for (const auto &I : ProfileMap) {
     398          22 :     const FunctionSamples &Profile = I.second;
     399          22 :     Builder.addRecord(Profile);
     400             :   }
     401          18 :   Summary = Builder.getSummary();
     402           9 : }

Generated by: LCOV version 1.13