1c572e92cSDiego Novillo //===- SampleProfWriter.cpp - Write LLVM sample profile data --------------===//
2c572e92cSDiego Novillo //
3c572e92cSDiego Novillo //                      The LLVM Compiler Infrastructure
4c572e92cSDiego Novillo //
5c572e92cSDiego Novillo // This file is distributed under the University of Illinois Open Source
6c572e92cSDiego Novillo // License. See LICENSE.TXT for details.
7c572e92cSDiego Novillo //
8c572e92cSDiego Novillo //===----------------------------------------------------------------------===//
9c572e92cSDiego Novillo //
10c572e92cSDiego Novillo // This file implements the class that writes LLVM sample profiles. It
11c572e92cSDiego Novillo // supports two file formats: text and binary. The textual representation
12c572e92cSDiego Novillo // is useful for debugging and testing purposes. The binary representation
13c572e92cSDiego Novillo // is more compact, resulting in smaller file sizes. However, they can
14c572e92cSDiego Novillo // both be used interchangeably.
15c572e92cSDiego Novillo //
16c572e92cSDiego Novillo // See lib/ProfileData/SampleProfReader.cpp for documentation on each of the
17c572e92cSDiego Novillo // supported formats.
18c572e92cSDiego Novillo //
19c572e92cSDiego Novillo //===----------------------------------------------------------------------===//
20c572e92cSDiego Novillo 
21c572e92cSDiego Novillo #include "llvm/ProfileData/SampleProfWriter.h"
22c572e92cSDiego Novillo #include "llvm/Support/Debug.h"
23c572e92cSDiego Novillo #include "llvm/Support/ErrorOr.h"
24c572e92cSDiego Novillo #include "llvm/Support/LEB128.h"
25c572e92cSDiego Novillo #include "llvm/Support/LineIterator.h"
26d9903888SChandler Carruth #include "llvm/Support/MemoryBuffer.h"
27c572e92cSDiego Novillo #include "llvm/Support/Regex.h"
28c572e92cSDiego Novillo 
29c572e92cSDiego Novillo using namespace llvm::sampleprof;
30c572e92cSDiego Novillo using namespace llvm;
31c572e92cSDiego Novillo 
32c572e92cSDiego Novillo /// \brief Write samples to a text file.
338e415a82SDiego Novillo ///
348e415a82SDiego Novillo /// Note: it may be tempting to implement this in terms of
35ef548d29SDiego Novillo /// FunctionSamples::print().  Please don't.  The dump functionality is intended
368e415a82SDiego Novillo /// for debugging and has no specified form.
378e415a82SDiego Novillo ///
388e415a82SDiego Novillo /// The format used here is more structured and deliberate because
398e415a82SDiego Novillo /// it needs to be parsed by the SampleProfileReaderText class.
4057d1dda5SDehao Chen std::error_code SampleProfileWriterText::write(const FunctionSamples &S) {
4151abea74SNathan Slingerland   auto &OS = *OutputStream;
4257d1dda5SDehao Chen   OS << S.getName() << ":" << S.getTotalSamples();
43aae1ed8eSDiego Novillo   if (Indent == 0)
44aae1ed8eSDiego Novillo     OS << ":" << S.getHeadSamples();
45aae1ed8eSDiego Novillo   OS << "\n";
46c572e92cSDiego Novillo 
47ef548d29SDiego Novillo   SampleSorter<LineLocation, SampleRecord> SortedSamples(S.getBodySamples());
48ef548d29SDiego Novillo   for (const auto &I : SortedSamples.get()) {
49ef548d29SDiego Novillo     LineLocation Loc = I->first;
50ef548d29SDiego Novillo     const SampleRecord &Sample = I->second;
51aae1ed8eSDiego Novillo     OS.indent(Indent + 1);
52c572e92cSDiego Novillo     if (Loc.Discriminator == 0)
53c572e92cSDiego Novillo       OS << Loc.LineOffset << ": ";
54c572e92cSDiego Novillo     else
55c572e92cSDiego Novillo       OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
56c572e92cSDiego Novillo 
57c572e92cSDiego Novillo     OS << Sample.getSamples();
58c572e92cSDiego Novillo 
59d5336ae2SDiego Novillo     for (const auto &J : Sample.getCallTargets())
60d5336ae2SDiego Novillo       OS << " " << J.first() << ":" << J.second;
61c572e92cSDiego Novillo     OS << "\n";
62c572e92cSDiego Novillo   }
63c572e92cSDiego Novillo 
6457d1dda5SDehao Chen   SampleSorter<LineLocation, FunctionSamples> SortedCallsiteSamples(
65ef548d29SDiego Novillo       S.getCallsiteSamples());
66aae1ed8eSDiego Novillo   Indent += 1;
67ef548d29SDiego Novillo   for (const auto &I : SortedCallsiteSamples.get()) {
6857d1dda5SDehao Chen     LineLocation Loc = I->first;
69ef548d29SDiego Novillo     const FunctionSamples &CalleeSamples = I->second;
70aae1ed8eSDiego Novillo     OS.indent(Indent);
71aae1ed8eSDiego Novillo     if (Loc.Discriminator == 0)
72aae1ed8eSDiego Novillo       OS << Loc.LineOffset << ": ";
73aae1ed8eSDiego Novillo     else
74aae1ed8eSDiego Novillo       OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
7557d1dda5SDehao Chen     if (std::error_code EC = write(CalleeSamples))
76760c5a8fSDiego Novillo       return EC;
77aae1ed8eSDiego Novillo   }
78aae1ed8eSDiego Novillo   Indent -= 1;
79aae1ed8eSDiego Novillo 
80760c5a8fSDiego Novillo   return sampleprof_error::success;
81c572e92cSDiego Novillo }
82c572e92cSDiego Novillo 
83760c5a8fSDiego Novillo std::error_code SampleProfileWriterBinary::writeNameIdx(StringRef FName) {
84760c5a8fSDiego Novillo   const auto &ret = NameTable.find(FName);
85760c5a8fSDiego Novillo   if (ret == NameTable.end())
86760c5a8fSDiego Novillo     return sampleprof_error::truncated_name_table;
8751abea74SNathan Slingerland   encodeULEB128(ret->second, *OutputStream);
88760c5a8fSDiego Novillo   return sampleprof_error::success;
89760c5a8fSDiego Novillo }
90c572e92cSDiego Novillo 
91760c5a8fSDiego Novillo void SampleProfileWriterBinary::addName(StringRef FName) {
92760c5a8fSDiego Novillo   auto NextIdx = NameTable.size();
93760c5a8fSDiego Novillo   NameTable.insert(std::make_pair(FName, NextIdx));
94760c5a8fSDiego Novillo }
95760c5a8fSDiego Novillo 
96760c5a8fSDiego Novillo void SampleProfileWriterBinary::addNames(const FunctionSamples &S) {
97760c5a8fSDiego Novillo   // Add all the names in indirect call targets.
98760c5a8fSDiego Novillo   for (const auto &I : S.getBodySamples()) {
99760c5a8fSDiego Novillo     const SampleRecord &Sample = I.second;
100760c5a8fSDiego Novillo     for (const auto &J : Sample.getCallTargets())
101760c5a8fSDiego Novillo       addName(J.first());
102760c5a8fSDiego Novillo   }
103760c5a8fSDiego Novillo 
104760c5a8fSDiego Novillo   // Recursively add all the names for inlined callsites.
105760c5a8fSDiego Novillo   for (const auto &J : S.getCallsiteSamples()) {
106760c5a8fSDiego Novillo     const FunctionSamples &CalleeSamples = J.second;
10757d1dda5SDehao Chen     addName(CalleeSamples.getName());
108760c5a8fSDiego Novillo     addNames(CalleeSamples);
109760c5a8fSDiego Novillo   }
110760c5a8fSDiego Novillo }
111760c5a8fSDiego Novillo 
112760c5a8fSDiego Novillo std::error_code SampleProfileWriterBinary::writeHeader(
113760c5a8fSDiego Novillo     const StringMap<FunctionSamples> &ProfileMap) {
11451abea74SNathan Slingerland   auto &OS = *OutputStream;
11551abea74SNathan Slingerland 
116760c5a8fSDiego Novillo   // Write file magic identifier.
117c572e92cSDiego Novillo   encodeULEB128(SPMagic(), OS);
118c572e92cSDiego Novillo   encodeULEB128(SPVersion(), OS);
119760c5a8fSDiego Novillo 
12040ee23dbSEaswaran Raman   computeSummary(ProfileMap);
12140ee23dbSEaswaran Raman   if (auto EC = writeSummary())
12240ee23dbSEaswaran Raman     return EC;
12340ee23dbSEaswaran Raman 
124760c5a8fSDiego Novillo   // Generate the name table for all the functions referenced in the profile.
125760c5a8fSDiego Novillo   for (const auto &I : ProfileMap) {
126760c5a8fSDiego Novillo     addName(I.first());
127760c5a8fSDiego Novillo     addNames(I.second);
128760c5a8fSDiego Novillo   }
129760c5a8fSDiego Novillo 
130760c5a8fSDiego Novillo   // Write out the name table.
131760c5a8fSDiego Novillo   encodeULEB128(NameTable.size(), OS);
132760c5a8fSDiego Novillo   for (auto N : NameTable) {
133760c5a8fSDiego Novillo     OS << N.first;
134760c5a8fSDiego Novillo     encodeULEB128(0, OS);
135760c5a8fSDiego Novillo   }
136760c5a8fSDiego Novillo   return sampleprof_error::success;
137c572e92cSDiego Novillo }
138c572e92cSDiego Novillo 
13940ee23dbSEaswaran Raman std::error_code SampleProfileWriterBinary::writeSummary() {
14040ee23dbSEaswaran Raman   auto &OS = *OutputStream;
1417cefdb81SEaswaran Raman   encodeULEB128(Summary->getTotalCount(), OS);
1427cefdb81SEaswaran Raman   encodeULEB128(Summary->getMaxCount(), OS);
1436f4903d9SEaswaran Raman   encodeULEB128(Summary->getMaxFunctionCount(), OS);
1447cefdb81SEaswaran Raman   encodeULEB128(Summary->getNumCounts(), OS);
14540ee23dbSEaswaran Raman   encodeULEB128(Summary->getNumFunctions(), OS);
14640ee23dbSEaswaran Raman   std::vector<ProfileSummaryEntry> &Entries = Summary->getDetailedSummary();
14740ee23dbSEaswaran Raman   encodeULEB128(Entries.size(), OS);
14840ee23dbSEaswaran Raman   for (auto Entry : Entries) {
14940ee23dbSEaswaran Raman     encodeULEB128(Entry.Cutoff, OS);
15040ee23dbSEaswaran Raman     encodeULEB128(Entry.MinCount, OS);
15140ee23dbSEaswaran Raman     encodeULEB128(Entry.NumCounts, OS);
15240ee23dbSEaswaran Raman   }
15340ee23dbSEaswaran Raman   return sampleprof_error::success;
15440ee23dbSEaswaran Raman }
15557d1dda5SDehao Chen std::error_code SampleProfileWriterBinary::writeBody(const FunctionSamples &S) {
15651abea74SNathan Slingerland   auto &OS = *OutputStream;
15751abea74SNathan Slingerland 
15857d1dda5SDehao Chen   if (std::error_code EC = writeNameIdx(S.getName()))
159760c5a8fSDiego Novillo     return EC;
160760c5a8fSDiego Novillo 
161c572e92cSDiego Novillo   encodeULEB128(S.getTotalSamples(), OS);
162a7f1e8efSDiego Novillo 
163a7f1e8efSDiego Novillo   // Emit all the body samples.
164b93483dbSDiego Novillo   encodeULEB128(S.getBodySamples().size(), OS);
165d5336ae2SDiego Novillo   for (const auto &I : S.getBodySamples()) {
166d5336ae2SDiego Novillo     LineLocation Loc = I.first;
167d5336ae2SDiego Novillo     const SampleRecord &Sample = I.second;
168c572e92cSDiego Novillo     encodeULEB128(Loc.LineOffset, OS);
169c572e92cSDiego Novillo     encodeULEB128(Loc.Discriminator, OS);
170c572e92cSDiego Novillo     encodeULEB128(Sample.getSamples(), OS);
171c572e92cSDiego Novillo     encodeULEB128(Sample.getCallTargets().size(), OS);
172d5336ae2SDiego Novillo     for (const auto &J : Sample.getCallTargets()) {
173760c5a8fSDiego Novillo       StringRef Callee = J.first();
17438be3330SDiego Novillo       uint64_t CalleeSamples = J.second;
175760c5a8fSDiego Novillo       if (std::error_code EC = writeNameIdx(Callee))
176760c5a8fSDiego Novillo         return EC;
177c572e92cSDiego Novillo       encodeULEB128(CalleeSamples, OS);
178c572e92cSDiego Novillo     }
179c572e92cSDiego Novillo   }
180c572e92cSDiego Novillo 
181a7f1e8efSDiego Novillo   // Recursively emit all the callsite samples.
182a7f1e8efSDiego Novillo   encodeULEB128(S.getCallsiteSamples().size(), OS);
183a7f1e8efSDiego Novillo   for (const auto &J : S.getCallsiteSamples()) {
18457d1dda5SDehao Chen     LineLocation Loc = J.first;
185a7f1e8efSDiego Novillo     const FunctionSamples &CalleeSamples = J.second;
186a7f1e8efSDiego Novillo     encodeULEB128(Loc.LineOffset, OS);
187a7f1e8efSDiego Novillo     encodeULEB128(Loc.Discriminator, OS);
18857d1dda5SDehao Chen     if (std::error_code EC = writeBody(CalleeSamples))
189760c5a8fSDiego Novillo       return EC;
190a7f1e8efSDiego Novillo   }
191a7f1e8efSDiego Novillo 
192760c5a8fSDiego Novillo   return sampleprof_error::success;
193c572e92cSDiego Novillo }
194d5336ae2SDiego Novillo 
195b93483dbSDiego Novillo /// \brief Write samples of a top-level function to a binary file.
196b93483dbSDiego Novillo ///
197b93483dbSDiego Novillo /// \returns true if the samples were written successfully, false otherwise.
19857d1dda5SDehao Chen std::error_code SampleProfileWriterBinary::write(const FunctionSamples &S) {
19951abea74SNathan Slingerland   encodeULEB128(S.getHeadSamples(), *OutputStream);
20057d1dda5SDehao Chen   return writeBody(S);
201b93483dbSDiego Novillo }
202b93483dbSDiego Novillo 
20351abea74SNathan Slingerland /// \brief Create a sample profile file writer based on the specified format.
204d5336ae2SDiego Novillo ///
205d5336ae2SDiego Novillo /// \param Filename The file to create.
206d5336ae2SDiego Novillo ///
207d5336ae2SDiego Novillo /// \param Writer The writer to instantiate according to the specified format.
208d5336ae2SDiego Novillo ///
209d5336ae2SDiego Novillo /// \param Format Encoding format for the profile file.
210d5336ae2SDiego Novillo ///
211d5336ae2SDiego Novillo /// \returns an error code indicating the status of the created writer.
212fcd55607SDiego Novillo ErrorOr<std::unique_ptr<SampleProfileWriter>>
213fcd55607SDiego Novillo SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) {
214d5336ae2SDiego Novillo   std::error_code EC;
21551abea74SNathan Slingerland   std::unique_ptr<raw_ostream> OS;
21651abea74SNathan Slingerland   if (Format == SPF_Binary)
21751abea74SNathan Slingerland     OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::F_None));
21851abea74SNathan Slingerland   else
21951abea74SNathan Slingerland     OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::F_Text));
22051abea74SNathan Slingerland   if (EC)
22151abea74SNathan Slingerland     return EC;
22251abea74SNathan Slingerland 
22351abea74SNathan Slingerland   return create(OS, Format);
22451abea74SNathan Slingerland }
22551abea74SNathan Slingerland 
22651abea74SNathan Slingerland /// \brief Create a sample profile stream writer based on the specified format.
22751abea74SNathan Slingerland ///
22851abea74SNathan Slingerland /// \param OS The output stream to store the profile data to.
22951abea74SNathan Slingerland ///
23051abea74SNathan Slingerland /// \param Writer The writer to instantiate according to the specified format.
23151abea74SNathan Slingerland ///
23251abea74SNathan Slingerland /// \param Format Encoding format for the profile file.
23351abea74SNathan Slingerland ///
23451abea74SNathan Slingerland /// \returns an error code indicating the status of the created writer.
23551abea74SNathan Slingerland ErrorOr<std::unique_ptr<SampleProfileWriter>>
23651abea74SNathan Slingerland SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
23751abea74SNathan Slingerland                             SampleProfileFormat Format) {
23851abea74SNathan Slingerland   std::error_code EC;
239fcd55607SDiego Novillo   std::unique_ptr<SampleProfileWriter> Writer;
240d5336ae2SDiego Novillo 
241d5336ae2SDiego Novillo   if (Format == SPF_Binary)
24251abea74SNathan Slingerland     Writer.reset(new SampleProfileWriterBinary(OS));
243d5336ae2SDiego Novillo   else if (Format == SPF_Text)
24451abea74SNathan Slingerland     Writer.reset(new SampleProfileWriterText(OS));
245760c5a8fSDiego Novillo   else if (Format == SPF_GCC)
246760c5a8fSDiego Novillo     EC = sampleprof_error::unsupported_writing_format;
247d5336ae2SDiego Novillo   else
248d5336ae2SDiego Novillo     EC = sampleprof_error::unrecognized_format;
249d5336ae2SDiego Novillo 
250fcd55607SDiego Novillo   if (EC)
251d5336ae2SDiego Novillo     return EC;
252fcd55607SDiego Novillo 
253fcd55607SDiego Novillo   return std::move(Writer);
254d5336ae2SDiego Novillo }
25540ee23dbSEaswaran Raman 
25640ee23dbSEaswaran Raman void SampleProfileWriter::computeSummary(
25740ee23dbSEaswaran Raman     const StringMap<FunctionSamples> &ProfileMap) {
258e5a17e3fSEaswaran Raman   SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
25940ee23dbSEaswaran Raman   for (const auto &I : ProfileMap) {
26040ee23dbSEaswaran Raman     const FunctionSamples &Profile = I.second;
261e5a17e3fSEaswaran Raman     Builder.addRecord(Profile);
26240ee23dbSEaswaran Raman   }
263*38de59e4SBenjamin Kramer   Summary = Builder.getSummary();
26440ee23dbSEaswaran Raman }
265