| //===- SampleProfWriter.cpp - Write LLVM sample profile data --------------===// | |
| // | |
| // 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 class that writes LLVM sample profiles. It | |
| // supports two file formats: text and binary. The textual representation | |
| // is useful for debugging and testing purposes. The binary representation | |
| // is more compact, resulting in smaller file sizes. However, they can | |
| // both be used interchangeably. | |
| // | |
| // See lib/ProfileData/SampleProfReader.cpp for documentation on each of the | |
| // supported formats. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/ProfileData/SampleProfWriter.h" | |
| #include "llvm/ADT/StringRef.h" | |
| #include "llvm/ProfileData/ProfileCommon.h" | |
| #include "llvm/ProfileData/SampleProf.h" | |
| #include "llvm/Support/ErrorOr.h" | |
| #include "llvm/Support/FileSystem.h" | |
| #include "llvm/Support/LEB128.h" | |
| #include "llvm/Support/MD5.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include <algorithm> | |
| #include <cstdint> | |
| #include <memory> | |
| #include <set> | |
| #include <system_error> | |
| #include <utility> | |
| #include <vector> | |
| using namespace llvm; | |
| using namespace sampleprof; | |
| std::error_code | |
| SampleProfileWriter::write(const StringMap<FunctionSamples> &ProfileMap) { | |
| if (std::error_code EC = writeHeader(ProfileMap)) | |
| return EC; | |
| // Sort the ProfileMap by total samples. | |
| typedef std::pair<StringRef, const FunctionSamples *> NameFunctionSamples; | |
| std::vector<NameFunctionSamples> V; | |
| for (const auto &I : ProfileMap) | |
| V.push_back(std::make_pair(I.getKey(), &I.second)); | |
| std::stable_sort( | |
| V.begin(), V.end(), | |
| [](const NameFunctionSamples &A, const NameFunctionSamples &B) { | |
| if (A.second->getTotalSamples() == B.second->getTotalSamples()) | |
| return A.first > B.first; | |
| return A.second->getTotalSamples() > B.second->getTotalSamples(); | |
| }); | |
| for (const auto &I : V) { | |
| if (std::error_code EC = write(*I.second)) | |
| return EC; | |
| } | |
| return sampleprof_error::success; | |
| } | |
| /// Write samples to a text file. | |
| /// | |
| /// Note: it may be tempting to implement this in terms of | |
| /// FunctionSamples::print(). Please don't. The dump functionality is intended | |
| /// for debugging and has no specified form. | |
| /// | |
| /// The format used here is more structured and deliberate because | |
| /// it needs to be parsed by the SampleProfileReaderText class. | |
| std::error_code SampleProfileWriterText::write(const FunctionSamples &S) { | |
| auto &OS = *OutputStream; | |
| OS << S.getName() << ":" << S.getTotalSamples(); | |
| if (Indent == 0) | |
| OS << ":" << S.getHeadSamples(); | |
| OS << "\n"; | |
| SampleSorter<LineLocation, SampleRecord> SortedSamples(S.getBodySamples()); | |
| for (const auto &I : SortedSamples.get()) { | |
| LineLocation Loc = I->first; | |
| const SampleRecord &Sample = I->second; | |
| OS.indent(Indent + 1); | |
| if (Loc.Discriminator == 0) | |
| OS << Loc.LineOffset << ": "; | |
| else | |
| OS << Loc.LineOffset << "." << Loc.Discriminator << ": "; | |
| OS << Sample.getSamples(); | |
| for (const auto &J : Sample.getCallTargets()) | |
| OS << " " << J.first() << ":" << J.second; | |
| OS << "\n"; | |
| } | |
| SampleSorter<LineLocation, FunctionSamplesMap> SortedCallsiteSamples( | |
| S.getCallsiteSamples()); | |
| Indent += 1; | |
| for (const auto &I : SortedCallsiteSamples.get()) | |
| for (const auto &FS : I->second) { | |
| LineLocation Loc = I->first; | |
| const FunctionSamples &CalleeSamples = FS.second; | |
| OS.indent(Indent); | |
| if (Loc.Discriminator == 0) | |
| OS << Loc.LineOffset << ": "; | |
| else | |
| OS << Loc.LineOffset << "." << Loc.Discriminator << ": "; | |
| if (std::error_code EC = write(CalleeSamples)) | |
| return EC; | |
| } | |
| Indent -= 1; | |
| return sampleprof_error::success; | |
| } | |
| std::error_code SampleProfileWriterBinary::writeNameIdx(StringRef FName) { | |
| const auto &ret = NameTable.find(FName); | |
| if (ret == NameTable.end()) | |
| return sampleprof_error::truncated_name_table; | |
| encodeULEB128(ret->second, *OutputStream); | |
| return sampleprof_error::success; | |
| } | |
| void SampleProfileWriterBinary::addName(StringRef FName) { | |
| NameTable.insert(std::make_pair(FName, 0)); | |
| } | |
| void SampleProfileWriterBinary::addNames(const FunctionSamples &S) { | |
| // Add all the names in indirect call targets. | |
| for (const auto &I : S.getBodySamples()) { | |
| const SampleRecord &Sample = I.second; | |
| for (const auto &J : Sample.getCallTargets()) | |
| addName(J.first()); | |
| } | |
| // Recursively add all the names for inlined callsites. | |
| for (const auto &J : S.getCallsiteSamples()) | |
| for (const auto &FS : J.second) { | |
| const FunctionSamples &CalleeSamples = FS.second; | |
| addName(CalleeSamples.getName()); | |
| addNames(CalleeSamples); | |
| } | |
| } | |
| void SampleProfileWriterBinary::stablizeNameTable(std::set<StringRef> &V) { | |
| // Sort the names to make NameTable deterministic. | |
| for (const auto &I : NameTable) | |
| V.insert(I.first); | |
| int i = 0; | |
| for (const StringRef &N : V) | |
| NameTable[N] = i++; | |
| } | |
| std::error_code SampleProfileWriterRawBinary::writeNameTable() { | |
| auto &OS = *OutputStream; | |
| std::set<StringRef> V; | |
| stablizeNameTable(V); | |
| // Write out the name table. | |
| encodeULEB128(NameTable.size(), OS); | |
| for (auto N : V) { | |
| OS << N; | |
| encodeULEB128(0, OS); | |
| } | |
| return sampleprof_error::success; | |
| } | |
| std::error_code SampleProfileWriterCompactBinary::writeNameTable() { | |
| auto &OS = *OutputStream; | |
| std::set<StringRef> V; | |
| stablizeNameTable(V); | |
| // Write out the name table. | |
| encodeULEB128(NameTable.size(), OS); | |
| for (auto N : V) { | |
| encodeULEB128(MD5Hash(N), OS); | |
| } | |
| return sampleprof_error::success; | |
| } | |
| std::error_code SampleProfileWriterRawBinary::writeMagicIdent() { | |
| auto &OS = *OutputStream; | |
| // Write file magic identifier. | |
| encodeULEB128(SPMagic(), OS); | |
| encodeULEB128(SPVersion(), OS); | |
| return sampleprof_error::success; | |
| } | |
| std::error_code SampleProfileWriterCompactBinary::writeMagicIdent() { | |
| auto &OS = *OutputStream; | |
| // Write file magic identifier. | |
| encodeULEB128(SPMagic(SPF_Compact_Binary), OS); | |
| encodeULEB128(SPVersion(), OS); | |
| return sampleprof_error::success; | |
| } | |
| std::error_code SampleProfileWriterBinary::writeHeader( | |
| const StringMap<FunctionSamples> &ProfileMap) { | |
| writeMagicIdent(); | |
| computeSummary(ProfileMap); | |
| if (auto EC = writeSummary()) | |
| return EC; | |
| // Generate the name table for all the functions referenced in the profile. | |
| for (const auto &I : ProfileMap) { | |
| addName(I.first()); | |
| addNames(I.second); | |
| } | |
| writeNameTable(); | |
| return sampleprof_error::success; | |
| } | |
| std::error_code SampleProfileWriterBinary::writeSummary() { | |
| auto &OS = *OutputStream; | |
| encodeULEB128(Summary->getTotalCount(), OS); | |
| encodeULEB128(Summary->getMaxCount(), OS); | |
| encodeULEB128(Summary->getMaxFunctionCount(), OS); | |
| encodeULEB128(Summary->getNumCounts(), OS); | |
| encodeULEB128(Summary->getNumFunctions(), OS); | |
| std::vector<ProfileSummaryEntry> &Entries = Summary->getDetailedSummary(); | |
| encodeULEB128(Entries.size(), OS); | |
| for (auto Entry : Entries) { | |
| encodeULEB128(Entry.Cutoff, OS); | |
| encodeULEB128(Entry.MinCount, OS); | |
| encodeULEB128(Entry.NumCounts, OS); | |
| } | |
| return sampleprof_error::success; | |
| } | |
| std::error_code SampleProfileWriterBinary::writeBody(const FunctionSamples &S) { | |
| auto &OS = *OutputStream; | |
| if (std::error_code EC = writeNameIdx(S.getName())) | |
| return EC; | |
| encodeULEB128(S.getTotalSamples(), OS); | |
| // Emit all the body samples. | |
| encodeULEB128(S.getBodySamples().size(), OS); | |
| for (const auto &I : S.getBodySamples()) { | |
| LineLocation Loc = I.first; | |
| const SampleRecord &Sample = I.second; | |
| encodeULEB128(Loc.LineOffset, OS); | |
| encodeULEB128(Loc.Discriminator, OS); | |
| encodeULEB128(Sample.getSamples(), OS); | |
| encodeULEB128(Sample.getCallTargets().size(), OS); | |
| for (const auto &J : Sample.getCallTargets()) { | |
| StringRef Callee = J.first(); | |
| uint64_t CalleeSamples = J.second; | |
| if (std::error_code EC = writeNameIdx(Callee)) | |
| return EC; | |
| encodeULEB128(CalleeSamples, OS); | |
| } | |
| } | |
| // Recursively emit all the callsite samples. | |
| uint64_t NumCallsites = 0; | |
| for (const auto &J : S.getCallsiteSamples()) | |
| NumCallsites += J.second.size(); | |
| encodeULEB128(NumCallsites, OS); | |
| for (const auto &J : S.getCallsiteSamples()) | |
| for (const auto &FS : J.second) { | |
| LineLocation Loc = J.first; | |
| const FunctionSamples &CalleeSamples = FS.second; | |
| encodeULEB128(Loc.LineOffset, OS); | |
| encodeULEB128(Loc.Discriminator, OS); | |
| if (std::error_code EC = writeBody(CalleeSamples)) | |
| return EC; | |
| } | |
| return sampleprof_error::success; | |
| } | |
| /// Write samples of a top-level function to a binary file. | |
| /// | |
| /// \returns true if the samples were written successfully, false otherwise. | |
| std::error_code SampleProfileWriterBinary::write(const FunctionSamples &S) { | |
| encodeULEB128(S.getHeadSamples(), *OutputStream); | |
| return writeBody(S); | |
| } | |
| /// Create a sample profile file writer based on the specified format. | |
| /// | |
| /// \param Filename The file to create. | |
| /// | |
| /// \param Format Encoding format for the profile file. | |
| /// | |
| /// \returns an error code indicating the status of the created writer. | |
| ErrorOr<std::unique_ptr<SampleProfileWriter>> | |
| SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) { | |
| std::error_code EC; | |
| std::unique_ptr<raw_ostream> OS; | |
| if (Format == SPF_Binary || Format == SPF_Compact_Binary) | |
| OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::F_None)); | |
| else | |
| OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::F_Text)); | |
| if (EC) | |
| return EC; | |
| return create(OS, Format); | |
| } | |
| /// Create a sample profile stream writer based on the specified format. | |
| /// | |
| /// \param OS The output stream to store the profile data to. | |
| /// | |
| /// \param Format Encoding format for the profile file. | |
| /// | |
| /// \returns an error code indicating the status of the created writer. | |
| ErrorOr<std::unique_ptr<SampleProfileWriter>> | |
| SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS, | |
| SampleProfileFormat Format) { | |
| std::error_code EC; | |
| std::unique_ptr<SampleProfileWriter> Writer; | |
| if (Format == SPF_Binary) | |
| Writer.reset(new SampleProfileWriterRawBinary(OS)); | |
| else if (Format == SPF_Compact_Binary) | |
| Writer.reset(new SampleProfileWriterCompactBinary(OS)); | |
| else if (Format == SPF_Text) | |
| Writer.reset(new SampleProfileWriterText(OS)); | |
| else if (Format == SPF_GCC) | |
| EC = sampleprof_error::unsupported_writing_format; | |
| else | |
| EC = sampleprof_error::unrecognized_format; | |
| if (EC) | |
| return EC; | |
| return std::move(Writer); | |
| } | |
| void SampleProfileWriter::computeSummary( | |
| const StringMap<FunctionSamples> &ProfileMap) { | |
| SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs); | |
| for (const auto &I : ProfileMap) { | |
| const FunctionSamples &Profile = I.second; | |
| Builder.addRecord(Profile); | |
| } | |
| Summary = Builder.getSummary(); | |
| } |