1277323Sdim//===- SampleProfWriter.cpp - Write LLVM sample profile data --------------===//
2277323Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6277323Sdim//
7277323Sdim//===----------------------------------------------------------------------===//
8277323Sdim//
9277323Sdim// This file implements the class that writes LLVM sample profiles. It
10277323Sdim// supports two file formats: text and binary. The textual representation
11277323Sdim// is useful for debugging and testing purposes. The binary representation
12277323Sdim// is more compact, resulting in smaller file sizes. However, they can
13277323Sdim// both be used interchangeably.
14277323Sdim//
15277323Sdim// See lib/ProfileData/SampleProfReader.cpp for documentation on each of the
16277323Sdim// supported formats.
17277323Sdim//
18277323Sdim//===----------------------------------------------------------------------===//
19277323Sdim
20277323Sdim#include "llvm/ProfileData/SampleProfWriter.h"
21321369Sdim#include "llvm/ADT/StringRef.h"
22321369Sdim#include "llvm/ProfileData/ProfileCommon.h"
23321369Sdim#include "llvm/ProfileData/SampleProf.h"
24360784Sdim#include "llvm/Support/Compression.h"
25344779Sdim#include "llvm/Support/Endian.h"
26344779Sdim#include "llvm/Support/EndianStream.h"
27277323Sdim#include "llvm/Support/ErrorOr.h"
28321369Sdim#include "llvm/Support/FileSystem.h"
29277323Sdim#include "llvm/Support/LEB128.h"
30341825Sdim#include "llvm/Support/MD5.h"
31321369Sdim#include "llvm/Support/raw_ostream.h"
32321369Sdim#include <algorithm>
33321369Sdim#include <cstdint>
34321369Sdim#include <memory>
35321369Sdim#include <set>
36321369Sdim#include <system_error>
37321369Sdim#include <utility>
38321369Sdim#include <vector>
39277323Sdim
40277323Sdimusing namespace llvm;
41321369Sdimusing namespace sampleprof;
42277323Sdim
43360784Sdimstd::error_code SampleProfileWriter::writeFuncProfiles(
44360784Sdim    const StringMap<FunctionSamples> &ProfileMap) {
45321369Sdim  // Sort the ProfileMap by total samples.
46321369Sdim  typedef std::pair<StringRef, const FunctionSamples *> NameFunctionSamples;
47321369Sdim  std::vector<NameFunctionSamples> V;
48321369Sdim  for (const auto &I : ProfileMap)
49321369Sdim    V.push_back(std::make_pair(I.getKey(), &I.second));
50321369Sdim
51353358Sdim  llvm::stable_sort(
52353358Sdim      V, [](const NameFunctionSamples &A, const NameFunctionSamples &B) {
53321369Sdim        if (A.second->getTotalSamples() == B.second->getTotalSamples())
54321369Sdim          return A.first > B.first;
55321369Sdim        return A.second->getTotalSamples() > B.second->getTotalSamples();
56321369Sdim      });
57321369Sdim
58321369Sdim  for (const auto &I : V) {
59360784Sdim    if (std::error_code EC = writeSample(*I.second))
60321369Sdim      return EC;
61321369Sdim  }
62321369Sdim  return sampleprof_error::success;
63321369Sdim}
64321369Sdim
65360784Sdimstd::error_code
66360784SdimSampleProfileWriter::write(const StringMap<FunctionSamples> &ProfileMap) {
67360784Sdim  if (std::error_code EC = writeHeader(ProfileMap))
68360784Sdim    return EC;
69360784Sdim
70360784Sdim  if (std::error_code EC = writeFuncProfiles(ProfileMap))
71360784Sdim    return EC;
72360784Sdim
73360784Sdim  return sampleprof_error::success;
74360784Sdim}
75360784Sdim
76360784SdimSecHdrTableEntry &
77360784SdimSampleProfileWriterExtBinaryBase::getEntryInLayout(SecType Type) {
78360784Sdim  auto SecIt = std::find_if(
79360784Sdim      SectionHdrLayout.begin(), SectionHdrLayout.end(),
80360784Sdim      [=](const auto &Entry) -> bool { return Entry.Type == Type; });
81360784Sdim  return *SecIt;
82360784Sdim}
83360784Sdim
84360784Sdim/// Return the current position and prepare to use it as the start
85360784Sdim/// position of a section.
86360784Sdimuint64_t SampleProfileWriterExtBinaryBase::markSectionStart(SecType Type) {
87360784Sdim  uint64_t SectionStart = OutputStream->tell();
88360784Sdim  auto &Entry = getEntryInLayout(Type);
89360784Sdim  // Use LocalBuf as a temporary output for writting data.
90360784Sdim  if (hasSecFlag(Entry, SecFlagCompress))
91360784Sdim    LocalBufStream.swap(OutputStream);
92360784Sdim  return SectionStart;
93360784Sdim}
94360784Sdim
95360784Sdimstd::error_code SampleProfileWriterExtBinaryBase::compressAndOutput() {
96360784Sdim  if (!llvm::zlib::isAvailable())
97360784Sdim    return sampleprof_error::zlib_unavailable;
98360784Sdim  std::string &UncompressedStrings =
99360784Sdim      static_cast<raw_string_ostream *>(LocalBufStream.get())->str();
100360784Sdim  if (UncompressedStrings.size() == 0)
101360784Sdim    return sampleprof_error::success;
102360784Sdim  auto &OS = *OutputStream;
103360784Sdim  SmallString<128> CompressedStrings;
104360784Sdim  llvm::Error E = zlib::compress(UncompressedStrings, CompressedStrings,
105360784Sdim                                 zlib::BestSizeCompression);
106360784Sdim  if (E)
107360784Sdim    return sampleprof_error::compress_failed;
108360784Sdim  encodeULEB128(UncompressedStrings.size(), OS);
109360784Sdim  encodeULEB128(CompressedStrings.size(), OS);
110360784Sdim  OS << CompressedStrings.str();
111360784Sdim  UncompressedStrings.clear();
112360784Sdim  return sampleprof_error::success;
113360784Sdim}
114360784Sdim
115360784Sdim/// Add a new section into section header table.
116360784Sdimstd::error_code
117360784SdimSampleProfileWriterExtBinaryBase::addNewSection(SecType Type,
118360784Sdim                                                uint64_t SectionStart) {
119360784Sdim  auto Entry = getEntryInLayout(Type);
120360784Sdim  if (hasSecFlag(Entry, SecFlagCompress)) {
121360784Sdim    LocalBufStream.swap(OutputStream);
122360784Sdim    if (std::error_code EC = compressAndOutput())
123360784Sdim      return EC;
124360784Sdim  }
125360784Sdim  SecHdrTable.push_back({Type, Entry.Flags, SectionStart - FileStart,
126360784Sdim                         OutputStream->tell() - SectionStart});
127360784Sdim  return sampleprof_error::success;
128360784Sdim}
129360784Sdim
130360784Sdimstd::error_code SampleProfileWriterExtBinaryBase::write(
131360784Sdim    const StringMap<FunctionSamples> &ProfileMap) {
132360784Sdim  if (std::error_code EC = writeHeader(ProfileMap))
133360784Sdim    return EC;
134360784Sdim
135360784Sdim  std::string LocalBuf;
136360784Sdim  LocalBufStream = std::make_unique<raw_string_ostream>(LocalBuf);
137360784Sdim  if (std::error_code EC = writeSections(ProfileMap))
138360784Sdim    return EC;
139360784Sdim
140360784Sdim  if (std::error_code EC = writeSecHdrTable())
141360784Sdim    return EC;
142360784Sdim
143360784Sdim  return sampleprof_error::success;
144360784Sdim}
145360784Sdim
146360784Sdimstd::error_code
147360784SdimSampleProfileWriterExtBinary::writeSample(const FunctionSamples &S) {
148360784Sdim  uint64_t Offset = OutputStream->tell();
149360784Sdim  StringRef Name = S.getName();
150360784Sdim  FuncOffsetTable[Name] = Offset - SecLBRProfileStart;
151360784Sdim  encodeULEB128(S.getHeadSamples(), *OutputStream);
152360784Sdim  return writeBody(S);
153360784Sdim}
154360784Sdim
155360784Sdimstd::error_code SampleProfileWriterExtBinary::writeFuncOffsetTable() {
156360784Sdim  auto &OS = *OutputStream;
157360784Sdim
158360784Sdim  // Write out the table size.
159360784Sdim  encodeULEB128(FuncOffsetTable.size(), OS);
160360784Sdim
161360784Sdim  // Write out FuncOffsetTable.
162360784Sdim  for (auto entry : FuncOffsetTable) {
163360784Sdim    writeNameIdx(entry.first);
164360784Sdim    encodeULEB128(entry.second, OS);
165360784Sdim  }
166360784Sdim  return sampleprof_error::success;
167360784Sdim}
168360784Sdim
169360784Sdimstd::error_code SampleProfileWriterExtBinary::writeSections(
170360784Sdim    const StringMap<FunctionSamples> &ProfileMap) {
171360784Sdim  uint64_t SectionStart = markSectionStart(SecProfSummary);
172360784Sdim  computeSummary(ProfileMap);
173360784Sdim  if (auto EC = writeSummary())
174360784Sdim    return EC;
175360784Sdim  if (std::error_code EC = addNewSection(SecProfSummary, SectionStart))
176360784Sdim    return EC;
177360784Sdim
178360784Sdim  // Generate the name table for all the functions referenced in the profile.
179360784Sdim  SectionStart = markSectionStart(SecNameTable);
180360784Sdim  for (const auto &I : ProfileMap) {
181360784Sdim    addName(I.first());
182360784Sdim    addNames(I.second);
183360784Sdim  }
184360784Sdim  writeNameTable();
185360784Sdim  if (std::error_code EC = addNewSection(SecNameTable, SectionStart))
186360784Sdim    return EC;
187360784Sdim
188360784Sdim  SectionStart = markSectionStart(SecLBRProfile);
189360784Sdim  SecLBRProfileStart = OutputStream->tell();
190360784Sdim  if (std::error_code EC = writeFuncProfiles(ProfileMap))
191360784Sdim    return EC;
192360784Sdim  if (std::error_code EC = addNewSection(SecLBRProfile, SectionStart))
193360784Sdim    return EC;
194360784Sdim
195360784Sdim  if (ProfSymList && ProfSymList->toCompress())
196360784Sdim    setToCompressSection(SecProfileSymbolList);
197360784Sdim
198360784Sdim  SectionStart = markSectionStart(SecProfileSymbolList);
199360784Sdim  if (ProfSymList && ProfSymList->size() > 0)
200360784Sdim    if (std::error_code EC = ProfSymList->write(*OutputStream))
201360784Sdim      return EC;
202360784Sdim  if (std::error_code EC = addNewSection(SecProfileSymbolList, SectionStart))
203360784Sdim    return EC;
204360784Sdim
205360784Sdim  SectionStart = markSectionStart(SecFuncOffsetTable);
206360784Sdim  if (std::error_code EC = writeFuncOffsetTable())
207360784Sdim    return EC;
208360784Sdim  if (std::error_code EC = addNewSection(SecFuncOffsetTable, SectionStart))
209360784Sdim    return EC;
210360784Sdim
211360784Sdim  return sampleprof_error::success;
212360784Sdim}
213360784Sdim
214344779Sdimstd::error_code SampleProfileWriterCompactBinary::write(
215344779Sdim    const StringMap<FunctionSamples> &ProfileMap) {
216344779Sdim  if (std::error_code EC = SampleProfileWriter::write(ProfileMap))
217344779Sdim    return EC;
218344779Sdim  if (std::error_code EC = writeFuncOffsetTable())
219344779Sdim    return EC;
220344779Sdim  return sampleprof_error::success;
221344779Sdim}
222344779Sdim
223341825Sdim/// Write samples to a text file.
224296417Sdim///
225296417Sdim/// Note: it may be tempting to implement this in terms of
226296417Sdim/// FunctionSamples::print().  Please don't.  The dump functionality is intended
227296417Sdim/// for debugging and has no specified form.
228296417Sdim///
229296417Sdim/// The format used here is more structured and deliberate because
230296417Sdim/// it needs to be parsed by the SampleProfileReaderText class.
231360784Sdimstd::error_code SampleProfileWriterText::writeSample(const FunctionSamples &S) {
232296417Sdim  auto &OS = *OutputStream;
233309124Sdim  OS << S.getName() << ":" << S.getTotalSamples();
234296417Sdim  if (Indent == 0)
235296417Sdim    OS << ":" << S.getHeadSamples();
236296417Sdim  OS << "\n";
237277323Sdim
238296417Sdim  SampleSorter<LineLocation, SampleRecord> SortedSamples(S.getBodySamples());
239296417Sdim  for (const auto &I : SortedSamples.get()) {
240296417Sdim    LineLocation Loc = I->first;
241296417Sdim    const SampleRecord &Sample = I->second;
242296417Sdim    OS.indent(Indent + 1);
243277323Sdim    if (Loc.Discriminator == 0)
244277323Sdim      OS << Loc.LineOffset << ": ";
245277323Sdim    else
246277323Sdim      OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
247277323Sdim
248277323Sdim    OS << Sample.getSamples();
249277323Sdim
250360784Sdim    for (const auto &J : Sample.getSortedCallTargets())
251360784Sdim      OS << " " << J.first << ":" << J.second;
252277323Sdim    OS << "\n";
253277323Sdim  }
254277323Sdim
255321369Sdim  SampleSorter<LineLocation, FunctionSamplesMap> SortedCallsiteSamples(
256296417Sdim      S.getCallsiteSamples());
257296417Sdim  Indent += 1;
258321369Sdim  for (const auto &I : SortedCallsiteSamples.get())
259321369Sdim    for (const auto &FS : I->second) {
260321369Sdim      LineLocation Loc = I->first;
261321369Sdim      const FunctionSamples &CalleeSamples = FS.second;
262321369Sdim      OS.indent(Indent);
263321369Sdim      if (Loc.Discriminator == 0)
264321369Sdim        OS << Loc.LineOffset << ": ";
265321369Sdim      else
266321369Sdim        OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
267360784Sdim      if (std::error_code EC = writeSample(CalleeSamples))
268321369Sdim        return EC;
269321369Sdim    }
270296417Sdim  Indent -= 1;
271296417Sdim
272296417Sdim  return sampleprof_error::success;
273277323Sdim}
274277323Sdim
275296417Sdimstd::error_code SampleProfileWriterBinary::writeNameIdx(StringRef FName) {
276296417Sdim  const auto &ret = NameTable.find(FName);
277296417Sdim  if (ret == NameTable.end())
278296417Sdim    return sampleprof_error::truncated_name_table;
279296417Sdim  encodeULEB128(ret->second, *OutputStream);
280296417Sdim  return sampleprof_error::success;
281296417Sdim}
282277323Sdim
283296417Sdimvoid SampleProfileWriterBinary::addName(StringRef FName) {
284321369Sdim  NameTable.insert(std::make_pair(FName, 0));
285296417Sdim}
286296417Sdim
287296417Sdimvoid SampleProfileWriterBinary::addNames(const FunctionSamples &S) {
288296417Sdim  // Add all the names in indirect call targets.
289296417Sdim  for (const auto &I : S.getBodySamples()) {
290296417Sdim    const SampleRecord &Sample = I.second;
291296417Sdim    for (const auto &J : Sample.getCallTargets())
292296417Sdim      addName(J.first());
293296417Sdim  }
294296417Sdim
295296417Sdim  // Recursively add all the names for inlined callsites.
296321369Sdim  for (const auto &J : S.getCallsiteSamples())
297321369Sdim    for (const auto &FS : J.second) {
298321369Sdim      const FunctionSamples &CalleeSamples = FS.second;
299321369Sdim      addName(CalleeSamples.getName());
300321369Sdim      addNames(CalleeSamples);
301321369Sdim    }
302296417Sdim}
303296417Sdim
304341825Sdimvoid SampleProfileWriterBinary::stablizeNameTable(std::set<StringRef> &V) {
305341825Sdim  // Sort the names to make NameTable deterministic.
306341825Sdim  for (const auto &I : NameTable)
307341825Sdim    V.insert(I.first);
308341825Sdim  int i = 0;
309341825Sdim  for (const StringRef &N : V)
310341825Sdim    NameTable[N] = i++;
311341825Sdim}
312341825Sdim
313360784Sdimstd::error_code SampleProfileWriterBinary::writeNameTable() {
314296417Sdim  auto &OS = *OutputStream;
315341825Sdim  std::set<StringRef> V;
316341825Sdim  stablizeNameTable(V);
317296417Sdim
318341825Sdim  // Write out the name table.
319341825Sdim  encodeULEB128(NameTable.size(), OS);
320341825Sdim  for (auto N : V) {
321341825Sdim    OS << N;
322341825Sdim    encodeULEB128(0, OS);
323341825Sdim  }
324341825Sdim  return sampleprof_error::success;
325341825Sdim}
326341825Sdim
327344779Sdimstd::error_code SampleProfileWriterCompactBinary::writeFuncOffsetTable() {
328344779Sdim  auto &OS = *OutputStream;
329344779Sdim
330344779Sdim  // Fill the slot remembered by TableOffset with the offset of FuncOffsetTable.
331344779Sdim  auto &OFS = static_cast<raw_fd_ostream &>(OS);
332344779Sdim  uint64_t FuncOffsetTableStart = OS.tell();
333344779Sdim  if (OFS.seek(TableOffset) == (uint64_t)-1)
334344779Sdim    return sampleprof_error::ostream_seek_unsupported;
335344779Sdim  support::endian::Writer Writer(*OutputStream, support::little);
336344779Sdim  Writer.write(FuncOffsetTableStart);
337344779Sdim  if (OFS.seek(FuncOffsetTableStart) == (uint64_t)-1)
338344779Sdim    return sampleprof_error::ostream_seek_unsupported;
339344779Sdim
340344779Sdim  // Write out the table size.
341344779Sdim  encodeULEB128(FuncOffsetTable.size(), OS);
342344779Sdim
343344779Sdim  // Write out FuncOffsetTable.
344344779Sdim  for (auto entry : FuncOffsetTable) {
345344779Sdim    writeNameIdx(entry.first);
346344779Sdim    encodeULEB128(entry.second, OS);
347344779Sdim  }
348344779Sdim  return sampleprof_error::success;
349344779Sdim}
350344779Sdim
351341825Sdimstd::error_code SampleProfileWriterCompactBinary::writeNameTable() {
352341825Sdim  auto &OS = *OutputStream;
353341825Sdim  std::set<StringRef> V;
354341825Sdim  stablizeNameTable(V);
355341825Sdim
356341825Sdim  // Write out the name table.
357341825Sdim  encodeULEB128(NameTable.size(), OS);
358341825Sdim  for (auto N : V) {
359341825Sdim    encodeULEB128(MD5Hash(N), OS);
360341825Sdim  }
361341825Sdim  return sampleprof_error::success;
362341825Sdim}
363341825Sdim
364360784Sdimstd::error_code
365360784SdimSampleProfileWriterBinary::writeMagicIdent(SampleProfileFormat Format) {
366341825Sdim  auto &OS = *OutputStream;
367296417Sdim  // Write file magic identifier.
368360784Sdim  encodeULEB128(SPMagic(Format), OS);
369277323Sdim  encodeULEB128(SPVersion(), OS);
370341825Sdim  return sampleprof_error::success;
371341825Sdim}
372296417Sdim
373341825Sdimstd::error_code SampleProfileWriterBinary::writeHeader(
374341825Sdim    const StringMap<FunctionSamples> &ProfileMap) {
375360784Sdim  writeMagicIdent(Format);
376341825Sdim
377309124Sdim  computeSummary(ProfileMap);
378309124Sdim  if (auto EC = writeSummary())
379309124Sdim    return EC;
380309124Sdim
381296417Sdim  // Generate the name table for all the functions referenced in the profile.
382296417Sdim  for (const auto &I : ProfileMap) {
383296417Sdim    addName(I.first());
384296417Sdim    addNames(I.second);
385296417Sdim  }
386296417Sdim
387341825Sdim  writeNameTable();
388296417Sdim  return sampleprof_error::success;
389277323Sdim}
390277323Sdim
391360784Sdimvoid SampleProfileWriterExtBinaryBase::setToCompressAllSections() {
392360784Sdim  for (auto &Entry : SectionHdrLayout)
393360784Sdim    addSecFlags(Entry, SecFlagCompress);
394360784Sdim}
395360784Sdim
396360784Sdimvoid SampleProfileWriterExtBinaryBase::setToCompressSection(SecType Type) {
397360784Sdim  addSectionFlags(Type, SecFlagCompress);
398360784Sdim}
399360784Sdim
400360784Sdimvoid SampleProfileWriterExtBinaryBase::addSectionFlags(SecType Type,
401360784Sdim                                                       SecFlags Flags) {
402360784Sdim  for (auto &Entry : SectionHdrLayout) {
403360784Sdim    if (Entry.Type == Type)
404360784Sdim      addSecFlags(Entry, Flags);
405360784Sdim  }
406360784Sdim}
407360784Sdim
408360784Sdimvoid SampleProfileWriterExtBinaryBase::allocSecHdrTable() {
409360784Sdim  support::endian::Writer Writer(*OutputStream, support::little);
410360784Sdim
411360784Sdim  Writer.write(static_cast<uint64_t>(SectionHdrLayout.size()));
412360784Sdim  SecHdrTableOffset = OutputStream->tell();
413360784Sdim  for (uint32_t i = 0; i < SectionHdrLayout.size(); i++) {
414360784Sdim    Writer.write(static_cast<uint64_t>(-1));
415360784Sdim    Writer.write(static_cast<uint64_t>(-1));
416360784Sdim    Writer.write(static_cast<uint64_t>(-1));
417360784Sdim    Writer.write(static_cast<uint64_t>(-1));
418360784Sdim  }
419360784Sdim}
420360784Sdim
421360784Sdimstd::error_code SampleProfileWriterExtBinaryBase::writeSecHdrTable() {
422360784Sdim  auto &OFS = static_cast<raw_fd_ostream &>(*OutputStream);
423360784Sdim  uint64_t Saved = OutputStream->tell();
424360784Sdim
425360784Sdim  // Set OutputStream to the location saved in SecHdrTableOffset.
426360784Sdim  if (OFS.seek(SecHdrTableOffset) == (uint64_t)-1)
427360784Sdim    return sampleprof_error::ostream_seek_unsupported;
428360784Sdim  support::endian::Writer Writer(*OutputStream, support::little);
429360784Sdim
430360784Sdim  DenseMap<uint32_t, uint32_t> IndexMap;
431360784Sdim  for (uint32_t i = 0; i < SecHdrTable.size(); i++) {
432360784Sdim    IndexMap.insert({static_cast<uint32_t>(SecHdrTable[i].Type), i});
433360784Sdim  }
434360784Sdim
435360784Sdim  // Write the section header table in the order specified in
436360784Sdim  // SectionHdrLayout. That is the sections order Reader will see.
437360784Sdim  // Note that the sections order in which Reader expects to read
438360784Sdim  // may be different from the order in which Writer is able to
439360784Sdim  // write, so we need to adjust the order in SecHdrTable to be
440360784Sdim  // consistent with SectionHdrLayout when we write SecHdrTable
441360784Sdim  // to the memory.
442360784Sdim  for (uint32_t i = 0; i < SectionHdrLayout.size(); i++) {
443360784Sdim    uint32_t idx = IndexMap[static_cast<uint32_t>(SectionHdrLayout[i].Type)];
444360784Sdim    Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Type));
445360784Sdim    Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Flags));
446360784Sdim    Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Offset));
447360784Sdim    Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Size));
448360784Sdim  }
449360784Sdim
450360784Sdim  // Reset OutputStream.
451360784Sdim  if (OFS.seek(Saved) == (uint64_t)-1)
452360784Sdim    return sampleprof_error::ostream_seek_unsupported;
453360784Sdim
454360784Sdim  return sampleprof_error::success;
455360784Sdim}
456360784Sdim
457360784Sdimstd::error_code SampleProfileWriterExtBinaryBase::writeHeader(
458360784Sdim    const StringMap<FunctionSamples> &ProfileMap) {
459360784Sdim  auto &OS = *OutputStream;
460360784Sdim  FileStart = OS.tell();
461360784Sdim  writeMagicIdent(Format);
462360784Sdim
463360784Sdim  allocSecHdrTable();
464360784Sdim  return sampleprof_error::success;
465360784Sdim}
466360784Sdim
467344779Sdimstd::error_code SampleProfileWriterCompactBinary::writeHeader(
468344779Sdim    const StringMap<FunctionSamples> &ProfileMap) {
469344779Sdim  support::endian::Writer Writer(*OutputStream, support::little);
470344779Sdim  if (auto EC = SampleProfileWriterBinary::writeHeader(ProfileMap))
471344779Sdim    return EC;
472344779Sdim
473344779Sdim  // Reserve a slot for the offset of function offset table. The slot will
474344779Sdim  // be populated with the offset of FuncOffsetTable later.
475344779Sdim  TableOffset = OutputStream->tell();
476344779Sdim  Writer.write(static_cast<uint64_t>(-2));
477344779Sdim  return sampleprof_error::success;
478344779Sdim}
479344779Sdim
480309124Sdimstd::error_code SampleProfileWriterBinary::writeSummary() {
481296417Sdim  auto &OS = *OutputStream;
482309124Sdim  encodeULEB128(Summary->getTotalCount(), OS);
483309124Sdim  encodeULEB128(Summary->getMaxCount(), OS);
484309124Sdim  encodeULEB128(Summary->getMaxFunctionCount(), OS);
485309124Sdim  encodeULEB128(Summary->getNumCounts(), OS);
486309124Sdim  encodeULEB128(Summary->getNumFunctions(), OS);
487309124Sdim  std::vector<ProfileSummaryEntry> &Entries = Summary->getDetailedSummary();
488309124Sdim  encodeULEB128(Entries.size(), OS);
489309124Sdim  for (auto Entry : Entries) {
490309124Sdim    encodeULEB128(Entry.Cutoff, OS);
491309124Sdim    encodeULEB128(Entry.MinCount, OS);
492309124Sdim    encodeULEB128(Entry.NumCounts, OS);
493309124Sdim  }
494309124Sdim  return sampleprof_error::success;
495309124Sdim}
496309124Sdimstd::error_code SampleProfileWriterBinary::writeBody(const FunctionSamples &S) {
497309124Sdim  auto &OS = *OutputStream;
498277323Sdim
499309124Sdim  if (std::error_code EC = writeNameIdx(S.getName()))
500296417Sdim    return EC;
501296417Sdim
502277323Sdim  encodeULEB128(S.getTotalSamples(), OS);
503296417Sdim
504296417Sdim  // Emit all the body samples.
505277323Sdim  encodeULEB128(S.getBodySamples().size(), OS);
506277323Sdim  for (const auto &I : S.getBodySamples()) {
507277323Sdim    LineLocation Loc = I.first;
508277323Sdim    const SampleRecord &Sample = I.second;
509277323Sdim    encodeULEB128(Loc.LineOffset, OS);
510277323Sdim    encodeULEB128(Loc.Discriminator, OS);
511277323Sdim    encodeULEB128(Sample.getSamples(), OS);
512277323Sdim    encodeULEB128(Sample.getCallTargets().size(), OS);
513360784Sdim    for (const auto &J : Sample.getSortedCallTargets()) {
514360784Sdim      StringRef Callee = J.first;
515296417Sdim      uint64_t CalleeSamples = J.second;
516296417Sdim      if (std::error_code EC = writeNameIdx(Callee))
517296417Sdim        return EC;
518277323Sdim      encodeULEB128(CalleeSamples, OS);
519277323Sdim    }
520277323Sdim  }
521277323Sdim
522296417Sdim  // Recursively emit all the callsite samples.
523327952Sdim  uint64_t NumCallsites = 0;
524321369Sdim  for (const auto &J : S.getCallsiteSamples())
525327952Sdim    NumCallsites += J.second.size();
526327952Sdim  encodeULEB128(NumCallsites, OS);
527327952Sdim  for (const auto &J : S.getCallsiteSamples())
528321369Sdim    for (const auto &FS : J.second) {
529321369Sdim      LineLocation Loc = J.first;
530321369Sdim      const FunctionSamples &CalleeSamples = FS.second;
531321369Sdim      encodeULEB128(Loc.LineOffset, OS);
532321369Sdim      encodeULEB128(Loc.Discriminator, OS);
533321369Sdim      if (std::error_code EC = writeBody(CalleeSamples))
534321369Sdim        return EC;
535321369Sdim    }
536296417Sdim
537296417Sdim  return sampleprof_error::success;
538277323Sdim}
539277323Sdim
540341825Sdim/// Write samples of a top-level function to a binary file.
541277323Sdim///
542296417Sdim/// \returns true if the samples were written successfully, false otherwise.
543360784Sdimstd::error_code
544360784SdimSampleProfileWriterBinary::writeSample(const FunctionSamples &S) {
545296417Sdim  encodeULEB128(S.getHeadSamples(), *OutputStream);
546309124Sdim  return writeBody(S);
547296417Sdim}
548296417Sdim
549344779Sdimstd::error_code
550360784SdimSampleProfileWriterCompactBinary::writeSample(const FunctionSamples &S) {
551344779Sdim  uint64_t Offset = OutputStream->tell();
552344779Sdim  StringRef Name = S.getName();
553344779Sdim  FuncOffsetTable[Name] = Offset;
554344779Sdim  encodeULEB128(S.getHeadSamples(), *OutputStream);
555344779Sdim  return writeBody(S);
556344779Sdim}
557344779Sdim
558341825Sdim/// Create a sample profile file writer based on the specified format.
559296417Sdim///
560277323Sdim/// \param Filename The file to create.
561277323Sdim///
562277323Sdim/// \param Format Encoding format for the profile file.
563277323Sdim///
564277323Sdim/// \returns an error code indicating the status of the created writer.
565277323SdimErrorOr<std::unique_ptr<SampleProfileWriter>>
566277323SdimSampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) {
567277323Sdim  std::error_code EC;
568296417Sdim  std::unique_ptr<raw_ostream> OS;
569360784Sdim  if (Format == SPF_Binary || Format == SPF_Ext_Binary ||
570360784Sdim      Format == SPF_Compact_Binary)
571360784Sdim    OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_None));
572296417Sdim  else
573360784Sdim    OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_Text));
574296417Sdim  if (EC)
575296417Sdim    return EC;
576296417Sdim
577296417Sdim  return create(OS, Format);
578296417Sdim}
579296417Sdim
580341825Sdim/// Create a sample profile stream writer based on the specified format.
581296417Sdim///
582296417Sdim/// \param OS The output stream to store the profile data to.
583296417Sdim///
584296417Sdim/// \param Format Encoding format for the profile file.
585296417Sdim///
586296417Sdim/// \returns an error code indicating the status of the created writer.
587296417SdimErrorOr<std::unique_ptr<SampleProfileWriter>>
588296417SdimSampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
589296417Sdim                            SampleProfileFormat Format) {
590296417Sdim  std::error_code EC;
591277323Sdim  std::unique_ptr<SampleProfileWriter> Writer;
592277323Sdim
593277323Sdim  if (Format == SPF_Binary)
594341825Sdim    Writer.reset(new SampleProfileWriterRawBinary(OS));
595360784Sdim  else if (Format == SPF_Ext_Binary)
596360784Sdim    Writer.reset(new SampleProfileWriterExtBinary(OS));
597341825Sdim  else if (Format == SPF_Compact_Binary)
598341825Sdim    Writer.reset(new SampleProfileWriterCompactBinary(OS));
599277323Sdim  else if (Format == SPF_Text)
600296417Sdim    Writer.reset(new SampleProfileWriterText(OS));
601296417Sdim  else if (Format == SPF_GCC)
602296417Sdim    EC = sampleprof_error::unsupported_writing_format;
603277323Sdim  else
604277323Sdim    EC = sampleprof_error::unrecognized_format;
605277323Sdim
606277323Sdim  if (EC)
607277323Sdim    return EC;
608277323Sdim
609360784Sdim  Writer->Format = Format;
610277323Sdim  return std::move(Writer);
611277323Sdim}
612309124Sdim
613309124Sdimvoid SampleProfileWriter::computeSummary(
614309124Sdim    const StringMap<FunctionSamples> &ProfileMap) {
615309124Sdim  SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
616309124Sdim  for (const auto &I : ProfileMap) {
617309124Sdim    const FunctionSamples &Profile = I.second;
618309124Sdim    Builder.addRecord(Profile);
619309124Sdim  }
620309124Sdim  Summary = Builder.getSummary();
621309124Sdim}
622