1*0b57cec5SDimitry Andric //===- SampleProfWriter.cpp - Write LLVM sample profile data --------------===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric // 9*0b57cec5SDimitry Andric // This file implements the class that writes LLVM sample profiles. It 10*0b57cec5SDimitry Andric // supports two file formats: text and binary. The textual representation 11*0b57cec5SDimitry Andric // is useful for debugging and testing purposes. The binary representation 12*0b57cec5SDimitry Andric // is more compact, resulting in smaller file sizes. However, they can 13*0b57cec5SDimitry Andric // both be used interchangeably. 14*0b57cec5SDimitry Andric // 15*0b57cec5SDimitry Andric // See lib/ProfileData/SampleProfReader.cpp for documentation on each of the 16*0b57cec5SDimitry Andric // supported formats. 17*0b57cec5SDimitry Andric // 18*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 19*0b57cec5SDimitry Andric 20*0b57cec5SDimitry Andric #include "llvm/ProfileData/SampleProfWriter.h" 21*0b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h" 22*0b57cec5SDimitry Andric #include "llvm/ProfileData/ProfileCommon.h" 23*0b57cec5SDimitry Andric #include "llvm/ProfileData/SampleProf.h" 248bcb0991SDimitry Andric #include "llvm/Support/Compression.h" 25*0b57cec5SDimitry Andric #include "llvm/Support/Endian.h" 26*0b57cec5SDimitry Andric #include "llvm/Support/EndianStream.h" 27*0b57cec5SDimitry Andric #include "llvm/Support/ErrorOr.h" 28*0b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h" 29*0b57cec5SDimitry Andric #include "llvm/Support/LEB128.h" 30*0b57cec5SDimitry Andric #include "llvm/Support/MD5.h" 31*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 32*0b57cec5SDimitry Andric #include <algorithm> 33*0b57cec5SDimitry Andric #include <cstdint> 34*0b57cec5SDimitry Andric #include <memory> 35*0b57cec5SDimitry Andric #include <set> 36*0b57cec5SDimitry Andric #include <system_error> 37*0b57cec5SDimitry Andric #include <utility> 38*0b57cec5SDimitry Andric #include <vector> 39*0b57cec5SDimitry Andric 40*0b57cec5SDimitry Andric using namespace llvm; 41*0b57cec5SDimitry Andric using namespace sampleprof; 42*0b57cec5SDimitry Andric 438bcb0991SDimitry Andric std::error_code SampleProfileWriter::writeFuncProfiles( 448bcb0991SDimitry Andric const StringMap<FunctionSamples> &ProfileMap) { 45*0b57cec5SDimitry Andric // Sort the ProfileMap by total samples. 46*0b57cec5SDimitry Andric typedef std::pair<StringRef, const FunctionSamples *> NameFunctionSamples; 47*0b57cec5SDimitry Andric std::vector<NameFunctionSamples> V; 48*0b57cec5SDimitry Andric for (const auto &I : ProfileMap) 49*0b57cec5SDimitry Andric V.push_back(std::make_pair(I.getKey(), &I.second)); 50*0b57cec5SDimitry Andric 51*0b57cec5SDimitry Andric llvm::stable_sort( 52*0b57cec5SDimitry Andric V, [](const NameFunctionSamples &A, const NameFunctionSamples &B) { 53*0b57cec5SDimitry Andric if (A.second->getTotalSamples() == B.second->getTotalSamples()) 54*0b57cec5SDimitry Andric return A.first > B.first; 55*0b57cec5SDimitry Andric return A.second->getTotalSamples() > B.second->getTotalSamples(); 56*0b57cec5SDimitry Andric }); 57*0b57cec5SDimitry Andric 58*0b57cec5SDimitry Andric for (const auto &I : V) { 598bcb0991SDimitry Andric if (std::error_code EC = writeSample(*I.second)) 60*0b57cec5SDimitry Andric return EC; 61*0b57cec5SDimitry Andric } 62*0b57cec5SDimitry Andric return sampleprof_error::success; 63*0b57cec5SDimitry Andric } 64*0b57cec5SDimitry Andric 658bcb0991SDimitry Andric std::error_code 668bcb0991SDimitry Andric SampleProfileWriter::write(const StringMap<FunctionSamples> &ProfileMap) { 678bcb0991SDimitry Andric if (std::error_code EC = writeHeader(ProfileMap)) 688bcb0991SDimitry Andric return EC; 698bcb0991SDimitry Andric 708bcb0991SDimitry Andric if (std::error_code EC = writeFuncProfiles(ProfileMap)) 718bcb0991SDimitry Andric return EC; 728bcb0991SDimitry Andric 738bcb0991SDimitry Andric return sampleprof_error::success; 748bcb0991SDimitry Andric } 758bcb0991SDimitry Andric 768bcb0991SDimitry Andric SecHdrTableEntry & 778bcb0991SDimitry Andric SampleProfileWriterExtBinaryBase::getEntryInLayout(SecType Type) { 788bcb0991SDimitry Andric auto SecIt = std::find_if( 798bcb0991SDimitry Andric SectionHdrLayout.begin(), SectionHdrLayout.end(), 808bcb0991SDimitry Andric [=](const auto &Entry) -> bool { return Entry.Type == Type; }); 818bcb0991SDimitry Andric return *SecIt; 828bcb0991SDimitry Andric } 838bcb0991SDimitry Andric 848bcb0991SDimitry Andric /// Return the current position and prepare to use it as the start 858bcb0991SDimitry Andric /// position of a section. 868bcb0991SDimitry Andric uint64_t SampleProfileWriterExtBinaryBase::markSectionStart(SecType Type) { 878bcb0991SDimitry Andric uint64_t SectionStart = OutputStream->tell(); 888bcb0991SDimitry Andric auto &Entry = getEntryInLayout(Type); 898bcb0991SDimitry Andric // Use LocalBuf as a temporary output for writting data. 908bcb0991SDimitry Andric if (hasSecFlag(Entry, SecFlagCompress)) 918bcb0991SDimitry Andric LocalBufStream.swap(OutputStream); 928bcb0991SDimitry Andric return SectionStart; 938bcb0991SDimitry Andric } 948bcb0991SDimitry Andric 958bcb0991SDimitry Andric std::error_code SampleProfileWriterExtBinaryBase::compressAndOutput() { 968bcb0991SDimitry Andric if (!llvm::zlib::isAvailable()) 978bcb0991SDimitry Andric return sampleprof_error::zlib_unavailable; 988bcb0991SDimitry Andric std::string &UncompressedStrings = 998bcb0991SDimitry Andric static_cast<raw_string_ostream *>(LocalBufStream.get())->str(); 1008bcb0991SDimitry Andric if (UncompressedStrings.size() == 0) 1018bcb0991SDimitry Andric return sampleprof_error::success; 1028bcb0991SDimitry Andric auto &OS = *OutputStream; 1038bcb0991SDimitry Andric SmallString<128> CompressedStrings; 1048bcb0991SDimitry Andric llvm::Error E = zlib::compress(UncompressedStrings, CompressedStrings, 1058bcb0991SDimitry Andric zlib::BestSizeCompression); 1068bcb0991SDimitry Andric if (E) 1078bcb0991SDimitry Andric return sampleprof_error::compress_failed; 1088bcb0991SDimitry Andric encodeULEB128(UncompressedStrings.size(), OS); 1098bcb0991SDimitry Andric encodeULEB128(CompressedStrings.size(), OS); 1108bcb0991SDimitry Andric OS << CompressedStrings.str(); 1118bcb0991SDimitry Andric UncompressedStrings.clear(); 1128bcb0991SDimitry Andric return sampleprof_error::success; 1138bcb0991SDimitry Andric } 1148bcb0991SDimitry Andric 1158bcb0991SDimitry Andric /// Add a new section into section header table. 1168bcb0991SDimitry Andric std::error_code 1178bcb0991SDimitry Andric SampleProfileWriterExtBinaryBase::addNewSection(SecType Type, 1188bcb0991SDimitry Andric uint64_t SectionStart) { 1198bcb0991SDimitry Andric auto Entry = getEntryInLayout(Type); 1208bcb0991SDimitry Andric if (hasSecFlag(Entry, SecFlagCompress)) { 1218bcb0991SDimitry Andric LocalBufStream.swap(OutputStream); 1228bcb0991SDimitry Andric if (std::error_code EC = compressAndOutput()) 1238bcb0991SDimitry Andric return EC; 1248bcb0991SDimitry Andric } 1258bcb0991SDimitry Andric SecHdrTable.push_back({Type, Entry.Flags, SectionStart - FileStart, 1268bcb0991SDimitry Andric OutputStream->tell() - SectionStart}); 1278bcb0991SDimitry Andric return sampleprof_error::success; 1288bcb0991SDimitry Andric } 1298bcb0991SDimitry Andric 1308bcb0991SDimitry Andric std::error_code SampleProfileWriterExtBinaryBase::write( 1318bcb0991SDimitry Andric const StringMap<FunctionSamples> &ProfileMap) { 1328bcb0991SDimitry Andric if (std::error_code EC = writeHeader(ProfileMap)) 1338bcb0991SDimitry Andric return EC; 1348bcb0991SDimitry Andric 1358bcb0991SDimitry Andric std::string LocalBuf; 1368bcb0991SDimitry Andric LocalBufStream = std::make_unique<raw_string_ostream>(LocalBuf); 1378bcb0991SDimitry Andric if (std::error_code EC = writeSections(ProfileMap)) 1388bcb0991SDimitry Andric return EC; 1398bcb0991SDimitry Andric 1408bcb0991SDimitry Andric if (std::error_code EC = writeSecHdrTable()) 1418bcb0991SDimitry Andric return EC; 1428bcb0991SDimitry Andric 1438bcb0991SDimitry Andric return sampleprof_error::success; 1448bcb0991SDimitry Andric } 1458bcb0991SDimitry Andric 1468bcb0991SDimitry Andric std::error_code 1478bcb0991SDimitry Andric SampleProfileWriterExtBinary::writeSample(const FunctionSamples &S) { 1488bcb0991SDimitry Andric uint64_t Offset = OutputStream->tell(); 1498bcb0991SDimitry Andric StringRef Name = S.getName(); 1508bcb0991SDimitry Andric FuncOffsetTable[Name] = Offset - SecLBRProfileStart; 1518bcb0991SDimitry Andric encodeULEB128(S.getHeadSamples(), *OutputStream); 1528bcb0991SDimitry Andric return writeBody(S); 1538bcb0991SDimitry Andric } 1548bcb0991SDimitry Andric 1558bcb0991SDimitry Andric std::error_code SampleProfileWriterExtBinary::writeFuncOffsetTable() { 1568bcb0991SDimitry Andric auto &OS = *OutputStream; 1578bcb0991SDimitry Andric 1588bcb0991SDimitry Andric // Write out the table size. 1598bcb0991SDimitry Andric encodeULEB128(FuncOffsetTable.size(), OS); 1608bcb0991SDimitry Andric 1618bcb0991SDimitry Andric // Write out FuncOffsetTable. 1628bcb0991SDimitry Andric for (auto entry : FuncOffsetTable) { 1638bcb0991SDimitry Andric writeNameIdx(entry.first); 1648bcb0991SDimitry Andric encodeULEB128(entry.second, OS); 1658bcb0991SDimitry Andric } 1668bcb0991SDimitry Andric return sampleprof_error::success; 1678bcb0991SDimitry Andric } 1688bcb0991SDimitry Andric 1698bcb0991SDimitry Andric std::error_code SampleProfileWriterExtBinary::writeSections( 1708bcb0991SDimitry Andric const StringMap<FunctionSamples> &ProfileMap) { 1718bcb0991SDimitry Andric uint64_t SectionStart = markSectionStart(SecProfSummary); 1728bcb0991SDimitry Andric computeSummary(ProfileMap); 1738bcb0991SDimitry Andric if (auto EC = writeSummary()) 1748bcb0991SDimitry Andric return EC; 1758bcb0991SDimitry Andric if (std::error_code EC = addNewSection(SecProfSummary, SectionStart)) 1768bcb0991SDimitry Andric return EC; 1778bcb0991SDimitry Andric 1788bcb0991SDimitry Andric // Generate the name table for all the functions referenced in the profile. 1798bcb0991SDimitry Andric SectionStart = markSectionStart(SecNameTable); 1808bcb0991SDimitry Andric for (const auto &I : ProfileMap) { 1818bcb0991SDimitry Andric addName(I.first()); 1828bcb0991SDimitry Andric addNames(I.second); 1838bcb0991SDimitry Andric } 1848bcb0991SDimitry Andric writeNameTable(); 1858bcb0991SDimitry Andric if (std::error_code EC = addNewSection(SecNameTable, SectionStart)) 1868bcb0991SDimitry Andric return EC; 1878bcb0991SDimitry Andric 1888bcb0991SDimitry Andric SectionStart = markSectionStart(SecLBRProfile); 1898bcb0991SDimitry Andric SecLBRProfileStart = OutputStream->tell(); 1908bcb0991SDimitry Andric if (std::error_code EC = writeFuncProfiles(ProfileMap)) 1918bcb0991SDimitry Andric return EC; 1928bcb0991SDimitry Andric if (std::error_code EC = addNewSection(SecLBRProfile, SectionStart)) 1938bcb0991SDimitry Andric return EC; 1948bcb0991SDimitry Andric 1958bcb0991SDimitry Andric if (ProfSymList && ProfSymList->toCompress()) 1968bcb0991SDimitry Andric setToCompressSection(SecProfileSymbolList); 1978bcb0991SDimitry Andric 1988bcb0991SDimitry Andric SectionStart = markSectionStart(SecProfileSymbolList); 1998bcb0991SDimitry Andric if (ProfSymList && ProfSymList->size() > 0) 2008bcb0991SDimitry Andric if (std::error_code EC = ProfSymList->write(*OutputStream)) 2018bcb0991SDimitry Andric return EC; 2028bcb0991SDimitry Andric if (std::error_code EC = addNewSection(SecProfileSymbolList, SectionStart)) 2038bcb0991SDimitry Andric return EC; 2048bcb0991SDimitry Andric 2058bcb0991SDimitry Andric SectionStart = markSectionStart(SecFuncOffsetTable); 2068bcb0991SDimitry Andric if (std::error_code EC = writeFuncOffsetTable()) 2078bcb0991SDimitry Andric return EC; 2088bcb0991SDimitry Andric if (std::error_code EC = addNewSection(SecFuncOffsetTable, SectionStart)) 2098bcb0991SDimitry Andric return EC; 2108bcb0991SDimitry Andric 2118bcb0991SDimitry Andric return sampleprof_error::success; 2128bcb0991SDimitry Andric } 2138bcb0991SDimitry Andric 214*0b57cec5SDimitry Andric std::error_code SampleProfileWriterCompactBinary::write( 215*0b57cec5SDimitry Andric const StringMap<FunctionSamples> &ProfileMap) { 216*0b57cec5SDimitry Andric if (std::error_code EC = SampleProfileWriter::write(ProfileMap)) 217*0b57cec5SDimitry Andric return EC; 218*0b57cec5SDimitry Andric if (std::error_code EC = writeFuncOffsetTable()) 219*0b57cec5SDimitry Andric return EC; 220*0b57cec5SDimitry Andric return sampleprof_error::success; 221*0b57cec5SDimitry Andric } 222*0b57cec5SDimitry Andric 223*0b57cec5SDimitry Andric /// Write samples to a text file. 224*0b57cec5SDimitry Andric /// 225*0b57cec5SDimitry Andric /// Note: it may be tempting to implement this in terms of 226*0b57cec5SDimitry Andric /// FunctionSamples::print(). Please don't. The dump functionality is intended 227*0b57cec5SDimitry Andric /// for debugging and has no specified form. 228*0b57cec5SDimitry Andric /// 229*0b57cec5SDimitry Andric /// The format used here is more structured and deliberate because 230*0b57cec5SDimitry Andric /// it needs to be parsed by the SampleProfileReaderText class. 2318bcb0991SDimitry Andric std::error_code SampleProfileWriterText::writeSample(const FunctionSamples &S) { 232*0b57cec5SDimitry Andric auto &OS = *OutputStream; 233*0b57cec5SDimitry Andric OS << S.getName() << ":" << S.getTotalSamples(); 234*0b57cec5SDimitry Andric if (Indent == 0) 235*0b57cec5SDimitry Andric OS << ":" << S.getHeadSamples(); 236*0b57cec5SDimitry Andric OS << "\n"; 237*0b57cec5SDimitry Andric 238*0b57cec5SDimitry Andric SampleSorter<LineLocation, SampleRecord> SortedSamples(S.getBodySamples()); 239*0b57cec5SDimitry Andric for (const auto &I : SortedSamples.get()) { 240*0b57cec5SDimitry Andric LineLocation Loc = I->first; 241*0b57cec5SDimitry Andric const SampleRecord &Sample = I->second; 242*0b57cec5SDimitry Andric OS.indent(Indent + 1); 243*0b57cec5SDimitry Andric if (Loc.Discriminator == 0) 244*0b57cec5SDimitry Andric OS << Loc.LineOffset << ": "; 245*0b57cec5SDimitry Andric else 246*0b57cec5SDimitry Andric OS << Loc.LineOffset << "." << Loc.Discriminator << ": "; 247*0b57cec5SDimitry Andric 248*0b57cec5SDimitry Andric OS << Sample.getSamples(); 249*0b57cec5SDimitry Andric 2508bcb0991SDimitry Andric for (const auto &J : Sample.getSortedCallTargets()) 2518bcb0991SDimitry Andric OS << " " << J.first << ":" << J.second; 252*0b57cec5SDimitry Andric OS << "\n"; 253*0b57cec5SDimitry Andric } 254*0b57cec5SDimitry Andric 255*0b57cec5SDimitry Andric SampleSorter<LineLocation, FunctionSamplesMap> SortedCallsiteSamples( 256*0b57cec5SDimitry Andric S.getCallsiteSamples()); 257*0b57cec5SDimitry Andric Indent += 1; 258*0b57cec5SDimitry Andric for (const auto &I : SortedCallsiteSamples.get()) 259*0b57cec5SDimitry Andric for (const auto &FS : I->second) { 260*0b57cec5SDimitry Andric LineLocation Loc = I->first; 261*0b57cec5SDimitry Andric const FunctionSamples &CalleeSamples = FS.second; 262*0b57cec5SDimitry Andric OS.indent(Indent); 263*0b57cec5SDimitry Andric if (Loc.Discriminator == 0) 264*0b57cec5SDimitry Andric OS << Loc.LineOffset << ": "; 265*0b57cec5SDimitry Andric else 266*0b57cec5SDimitry Andric OS << Loc.LineOffset << "." << Loc.Discriminator << ": "; 2678bcb0991SDimitry Andric if (std::error_code EC = writeSample(CalleeSamples)) 268*0b57cec5SDimitry Andric return EC; 269*0b57cec5SDimitry Andric } 270*0b57cec5SDimitry Andric Indent -= 1; 271*0b57cec5SDimitry Andric 272*0b57cec5SDimitry Andric return sampleprof_error::success; 273*0b57cec5SDimitry Andric } 274*0b57cec5SDimitry Andric 275*0b57cec5SDimitry Andric std::error_code SampleProfileWriterBinary::writeNameIdx(StringRef FName) { 276*0b57cec5SDimitry Andric const auto &ret = NameTable.find(FName); 277*0b57cec5SDimitry Andric if (ret == NameTable.end()) 278*0b57cec5SDimitry Andric return sampleprof_error::truncated_name_table; 279*0b57cec5SDimitry Andric encodeULEB128(ret->second, *OutputStream); 280*0b57cec5SDimitry Andric return sampleprof_error::success; 281*0b57cec5SDimitry Andric } 282*0b57cec5SDimitry Andric 283*0b57cec5SDimitry Andric void SampleProfileWriterBinary::addName(StringRef FName) { 284*0b57cec5SDimitry Andric NameTable.insert(std::make_pair(FName, 0)); 285*0b57cec5SDimitry Andric } 286*0b57cec5SDimitry Andric 287*0b57cec5SDimitry Andric void SampleProfileWriterBinary::addNames(const FunctionSamples &S) { 288*0b57cec5SDimitry Andric // Add all the names in indirect call targets. 289*0b57cec5SDimitry Andric for (const auto &I : S.getBodySamples()) { 290*0b57cec5SDimitry Andric const SampleRecord &Sample = I.second; 291*0b57cec5SDimitry Andric for (const auto &J : Sample.getCallTargets()) 292*0b57cec5SDimitry Andric addName(J.first()); 293*0b57cec5SDimitry Andric } 294*0b57cec5SDimitry Andric 295*0b57cec5SDimitry Andric // Recursively add all the names for inlined callsites. 296*0b57cec5SDimitry Andric for (const auto &J : S.getCallsiteSamples()) 297*0b57cec5SDimitry Andric for (const auto &FS : J.second) { 298*0b57cec5SDimitry Andric const FunctionSamples &CalleeSamples = FS.second; 299*0b57cec5SDimitry Andric addName(CalleeSamples.getName()); 300*0b57cec5SDimitry Andric addNames(CalleeSamples); 301*0b57cec5SDimitry Andric } 302*0b57cec5SDimitry Andric } 303*0b57cec5SDimitry Andric 304*0b57cec5SDimitry Andric void SampleProfileWriterBinary::stablizeNameTable(std::set<StringRef> &V) { 305*0b57cec5SDimitry Andric // Sort the names to make NameTable deterministic. 306*0b57cec5SDimitry Andric for (const auto &I : NameTable) 307*0b57cec5SDimitry Andric V.insert(I.first); 308*0b57cec5SDimitry Andric int i = 0; 309*0b57cec5SDimitry Andric for (const StringRef &N : V) 310*0b57cec5SDimitry Andric NameTable[N] = i++; 311*0b57cec5SDimitry Andric } 312*0b57cec5SDimitry Andric 3138bcb0991SDimitry Andric std::error_code SampleProfileWriterBinary::writeNameTable() { 314*0b57cec5SDimitry Andric auto &OS = *OutputStream; 315*0b57cec5SDimitry Andric std::set<StringRef> V; 316*0b57cec5SDimitry Andric stablizeNameTable(V); 317*0b57cec5SDimitry Andric 318*0b57cec5SDimitry Andric // Write out the name table. 319*0b57cec5SDimitry Andric encodeULEB128(NameTable.size(), OS); 320*0b57cec5SDimitry Andric for (auto N : V) { 321*0b57cec5SDimitry Andric OS << N; 322*0b57cec5SDimitry Andric encodeULEB128(0, OS); 323*0b57cec5SDimitry Andric } 324*0b57cec5SDimitry Andric return sampleprof_error::success; 325*0b57cec5SDimitry Andric } 326*0b57cec5SDimitry Andric 327*0b57cec5SDimitry Andric std::error_code SampleProfileWriterCompactBinary::writeFuncOffsetTable() { 328*0b57cec5SDimitry Andric auto &OS = *OutputStream; 329*0b57cec5SDimitry Andric 330*0b57cec5SDimitry Andric // Fill the slot remembered by TableOffset with the offset of FuncOffsetTable. 331*0b57cec5SDimitry Andric auto &OFS = static_cast<raw_fd_ostream &>(OS); 332*0b57cec5SDimitry Andric uint64_t FuncOffsetTableStart = OS.tell(); 333*0b57cec5SDimitry Andric if (OFS.seek(TableOffset) == (uint64_t)-1) 334*0b57cec5SDimitry Andric return sampleprof_error::ostream_seek_unsupported; 335*0b57cec5SDimitry Andric support::endian::Writer Writer(*OutputStream, support::little); 336*0b57cec5SDimitry Andric Writer.write(FuncOffsetTableStart); 337*0b57cec5SDimitry Andric if (OFS.seek(FuncOffsetTableStart) == (uint64_t)-1) 338*0b57cec5SDimitry Andric return sampleprof_error::ostream_seek_unsupported; 339*0b57cec5SDimitry Andric 340*0b57cec5SDimitry Andric // Write out the table size. 341*0b57cec5SDimitry Andric encodeULEB128(FuncOffsetTable.size(), OS); 342*0b57cec5SDimitry Andric 343*0b57cec5SDimitry Andric // Write out FuncOffsetTable. 344*0b57cec5SDimitry Andric for (auto entry : FuncOffsetTable) { 345*0b57cec5SDimitry Andric writeNameIdx(entry.first); 346*0b57cec5SDimitry Andric encodeULEB128(entry.second, OS); 347*0b57cec5SDimitry Andric } 348*0b57cec5SDimitry Andric return sampleprof_error::success; 349*0b57cec5SDimitry Andric } 350*0b57cec5SDimitry Andric 351*0b57cec5SDimitry Andric std::error_code SampleProfileWriterCompactBinary::writeNameTable() { 352*0b57cec5SDimitry Andric auto &OS = *OutputStream; 353*0b57cec5SDimitry Andric std::set<StringRef> V; 354*0b57cec5SDimitry Andric stablizeNameTable(V); 355*0b57cec5SDimitry Andric 356*0b57cec5SDimitry Andric // Write out the name table. 357*0b57cec5SDimitry Andric encodeULEB128(NameTable.size(), OS); 358*0b57cec5SDimitry Andric for (auto N : V) { 359*0b57cec5SDimitry Andric encodeULEB128(MD5Hash(N), OS); 360*0b57cec5SDimitry Andric } 361*0b57cec5SDimitry Andric return sampleprof_error::success; 362*0b57cec5SDimitry Andric } 363*0b57cec5SDimitry Andric 3648bcb0991SDimitry Andric std::error_code 3658bcb0991SDimitry Andric SampleProfileWriterBinary::writeMagicIdent(SampleProfileFormat Format) { 366*0b57cec5SDimitry Andric auto &OS = *OutputStream; 367*0b57cec5SDimitry Andric // Write file magic identifier. 3688bcb0991SDimitry Andric encodeULEB128(SPMagic(Format), OS); 369*0b57cec5SDimitry Andric encodeULEB128(SPVersion(), OS); 370*0b57cec5SDimitry Andric return sampleprof_error::success; 371*0b57cec5SDimitry Andric } 372*0b57cec5SDimitry Andric 373*0b57cec5SDimitry Andric std::error_code SampleProfileWriterBinary::writeHeader( 374*0b57cec5SDimitry Andric const StringMap<FunctionSamples> &ProfileMap) { 3758bcb0991SDimitry Andric writeMagicIdent(Format); 376*0b57cec5SDimitry Andric 377*0b57cec5SDimitry Andric computeSummary(ProfileMap); 378*0b57cec5SDimitry Andric if (auto EC = writeSummary()) 379*0b57cec5SDimitry Andric return EC; 380*0b57cec5SDimitry Andric 381*0b57cec5SDimitry Andric // Generate the name table for all the functions referenced in the profile. 382*0b57cec5SDimitry Andric for (const auto &I : ProfileMap) { 383*0b57cec5SDimitry Andric addName(I.first()); 384*0b57cec5SDimitry Andric addNames(I.second); 385*0b57cec5SDimitry Andric } 386*0b57cec5SDimitry Andric 387*0b57cec5SDimitry Andric writeNameTable(); 388*0b57cec5SDimitry Andric return sampleprof_error::success; 389*0b57cec5SDimitry Andric } 390*0b57cec5SDimitry Andric 3918bcb0991SDimitry Andric void SampleProfileWriterExtBinaryBase::setToCompressAllSections() { 3928bcb0991SDimitry Andric for (auto &Entry : SectionHdrLayout) 3938bcb0991SDimitry Andric addSecFlags(Entry, SecFlagCompress); 3948bcb0991SDimitry Andric } 3958bcb0991SDimitry Andric 3968bcb0991SDimitry Andric void SampleProfileWriterExtBinaryBase::setToCompressSection(SecType Type) { 3978bcb0991SDimitry Andric addSectionFlags(Type, SecFlagCompress); 3988bcb0991SDimitry Andric } 3998bcb0991SDimitry Andric 4008bcb0991SDimitry Andric void SampleProfileWriterExtBinaryBase::addSectionFlags(SecType Type, 4018bcb0991SDimitry Andric SecFlags Flags) { 4028bcb0991SDimitry Andric for (auto &Entry : SectionHdrLayout) { 4038bcb0991SDimitry Andric if (Entry.Type == Type) 4048bcb0991SDimitry Andric addSecFlags(Entry, Flags); 4058bcb0991SDimitry Andric } 4068bcb0991SDimitry Andric } 4078bcb0991SDimitry Andric 4088bcb0991SDimitry Andric void SampleProfileWriterExtBinaryBase::allocSecHdrTable() { 4098bcb0991SDimitry Andric support::endian::Writer Writer(*OutputStream, support::little); 4108bcb0991SDimitry Andric 4118bcb0991SDimitry Andric Writer.write(static_cast<uint64_t>(SectionHdrLayout.size())); 4128bcb0991SDimitry Andric SecHdrTableOffset = OutputStream->tell(); 4138bcb0991SDimitry Andric for (uint32_t i = 0; i < SectionHdrLayout.size(); i++) { 4148bcb0991SDimitry Andric Writer.write(static_cast<uint64_t>(-1)); 4158bcb0991SDimitry Andric Writer.write(static_cast<uint64_t>(-1)); 4168bcb0991SDimitry Andric Writer.write(static_cast<uint64_t>(-1)); 4178bcb0991SDimitry Andric Writer.write(static_cast<uint64_t>(-1)); 4188bcb0991SDimitry Andric } 4198bcb0991SDimitry Andric } 4208bcb0991SDimitry Andric 4218bcb0991SDimitry Andric std::error_code SampleProfileWriterExtBinaryBase::writeSecHdrTable() { 4228bcb0991SDimitry Andric auto &OFS = static_cast<raw_fd_ostream &>(*OutputStream); 4238bcb0991SDimitry Andric uint64_t Saved = OutputStream->tell(); 4248bcb0991SDimitry Andric 4258bcb0991SDimitry Andric // Set OutputStream to the location saved in SecHdrTableOffset. 4268bcb0991SDimitry Andric if (OFS.seek(SecHdrTableOffset) == (uint64_t)-1) 4278bcb0991SDimitry Andric return sampleprof_error::ostream_seek_unsupported; 4288bcb0991SDimitry Andric support::endian::Writer Writer(*OutputStream, support::little); 4298bcb0991SDimitry Andric 4308bcb0991SDimitry Andric DenseMap<uint32_t, uint32_t> IndexMap; 4318bcb0991SDimitry Andric for (uint32_t i = 0; i < SecHdrTable.size(); i++) { 4328bcb0991SDimitry Andric IndexMap.insert({static_cast<uint32_t>(SecHdrTable[i].Type), i}); 4338bcb0991SDimitry Andric } 4348bcb0991SDimitry Andric 4358bcb0991SDimitry Andric // Write the section header table in the order specified in 4368bcb0991SDimitry Andric // SectionHdrLayout. That is the sections order Reader will see. 4378bcb0991SDimitry Andric // Note that the sections order in which Reader expects to read 4388bcb0991SDimitry Andric // may be different from the order in which Writer is able to 4398bcb0991SDimitry Andric // write, so we need to adjust the order in SecHdrTable to be 4408bcb0991SDimitry Andric // consistent with SectionHdrLayout when we write SecHdrTable 4418bcb0991SDimitry Andric // to the memory. 4428bcb0991SDimitry Andric for (uint32_t i = 0; i < SectionHdrLayout.size(); i++) { 4438bcb0991SDimitry Andric uint32_t idx = IndexMap[static_cast<uint32_t>(SectionHdrLayout[i].Type)]; 4448bcb0991SDimitry Andric Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Type)); 4458bcb0991SDimitry Andric Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Flags)); 4468bcb0991SDimitry Andric Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Offset)); 4478bcb0991SDimitry Andric Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Size)); 4488bcb0991SDimitry Andric } 4498bcb0991SDimitry Andric 4508bcb0991SDimitry Andric // Reset OutputStream. 4518bcb0991SDimitry Andric if (OFS.seek(Saved) == (uint64_t)-1) 4528bcb0991SDimitry Andric return sampleprof_error::ostream_seek_unsupported; 4538bcb0991SDimitry Andric 4548bcb0991SDimitry Andric return sampleprof_error::success; 4558bcb0991SDimitry Andric } 4568bcb0991SDimitry Andric 4578bcb0991SDimitry Andric std::error_code SampleProfileWriterExtBinaryBase::writeHeader( 4588bcb0991SDimitry Andric const StringMap<FunctionSamples> &ProfileMap) { 4598bcb0991SDimitry Andric auto &OS = *OutputStream; 4608bcb0991SDimitry Andric FileStart = OS.tell(); 4618bcb0991SDimitry Andric writeMagicIdent(Format); 4628bcb0991SDimitry Andric 4638bcb0991SDimitry Andric allocSecHdrTable(); 4648bcb0991SDimitry Andric return sampleprof_error::success; 4658bcb0991SDimitry Andric } 4668bcb0991SDimitry Andric 467*0b57cec5SDimitry Andric std::error_code SampleProfileWriterCompactBinary::writeHeader( 468*0b57cec5SDimitry Andric const StringMap<FunctionSamples> &ProfileMap) { 469*0b57cec5SDimitry Andric support::endian::Writer Writer(*OutputStream, support::little); 470*0b57cec5SDimitry Andric if (auto EC = SampleProfileWriterBinary::writeHeader(ProfileMap)) 471*0b57cec5SDimitry Andric return EC; 472*0b57cec5SDimitry Andric 473*0b57cec5SDimitry Andric // Reserve a slot for the offset of function offset table. The slot will 474*0b57cec5SDimitry Andric // be populated with the offset of FuncOffsetTable later. 475*0b57cec5SDimitry Andric TableOffset = OutputStream->tell(); 476*0b57cec5SDimitry Andric Writer.write(static_cast<uint64_t>(-2)); 477*0b57cec5SDimitry Andric return sampleprof_error::success; 478*0b57cec5SDimitry Andric } 479*0b57cec5SDimitry Andric 480*0b57cec5SDimitry Andric std::error_code SampleProfileWriterBinary::writeSummary() { 481*0b57cec5SDimitry Andric auto &OS = *OutputStream; 482*0b57cec5SDimitry Andric encodeULEB128(Summary->getTotalCount(), OS); 483*0b57cec5SDimitry Andric encodeULEB128(Summary->getMaxCount(), OS); 484*0b57cec5SDimitry Andric encodeULEB128(Summary->getMaxFunctionCount(), OS); 485*0b57cec5SDimitry Andric encodeULEB128(Summary->getNumCounts(), OS); 486*0b57cec5SDimitry Andric encodeULEB128(Summary->getNumFunctions(), OS); 487*0b57cec5SDimitry Andric std::vector<ProfileSummaryEntry> &Entries = Summary->getDetailedSummary(); 488*0b57cec5SDimitry Andric encodeULEB128(Entries.size(), OS); 489*0b57cec5SDimitry Andric for (auto Entry : Entries) { 490*0b57cec5SDimitry Andric encodeULEB128(Entry.Cutoff, OS); 491*0b57cec5SDimitry Andric encodeULEB128(Entry.MinCount, OS); 492*0b57cec5SDimitry Andric encodeULEB128(Entry.NumCounts, OS); 493*0b57cec5SDimitry Andric } 494*0b57cec5SDimitry Andric return sampleprof_error::success; 495*0b57cec5SDimitry Andric } 496*0b57cec5SDimitry Andric std::error_code SampleProfileWriterBinary::writeBody(const FunctionSamples &S) { 497*0b57cec5SDimitry Andric auto &OS = *OutputStream; 498*0b57cec5SDimitry Andric 499*0b57cec5SDimitry Andric if (std::error_code EC = writeNameIdx(S.getName())) 500*0b57cec5SDimitry Andric return EC; 501*0b57cec5SDimitry Andric 502*0b57cec5SDimitry Andric encodeULEB128(S.getTotalSamples(), OS); 503*0b57cec5SDimitry Andric 504*0b57cec5SDimitry Andric // Emit all the body samples. 505*0b57cec5SDimitry Andric encodeULEB128(S.getBodySamples().size(), OS); 506*0b57cec5SDimitry Andric for (const auto &I : S.getBodySamples()) { 507*0b57cec5SDimitry Andric LineLocation Loc = I.first; 508*0b57cec5SDimitry Andric const SampleRecord &Sample = I.second; 509*0b57cec5SDimitry Andric encodeULEB128(Loc.LineOffset, OS); 510*0b57cec5SDimitry Andric encodeULEB128(Loc.Discriminator, OS); 511*0b57cec5SDimitry Andric encodeULEB128(Sample.getSamples(), OS); 512*0b57cec5SDimitry Andric encodeULEB128(Sample.getCallTargets().size(), OS); 5138bcb0991SDimitry Andric for (const auto &J : Sample.getSortedCallTargets()) { 5148bcb0991SDimitry Andric StringRef Callee = J.first; 515*0b57cec5SDimitry Andric uint64_t CalleeSamples = J.second; 516*0b57cec5SDimitry Andric if (std::error_code EC = writeNameIdx(Callee)) 517*0b57cec5SDimitry Andric return EC; 518*0b57cec5SDimitry Andric encodeULEB128(CalleeSamples, OS); 519*0b57cec5SDimitry Andric } 520*0b57cec5SDimitry Andric } 521*0b57cec5SDimitry Andric 522*0b57cec5SDimitry Andric // Recursively emit all the callsite samples. 523*0b57cec5SDimitry Andric uint64_t NumCallsites = 0; 524*0b57cec5SDimitry Andric for (const auto &J : S.getCallsiteSamples()) 525*0b57cec5SDimitry Andric NumCallsites += J.second.size(); 526*0b57cec5SDimitry Andric encodeULEB128(NumCallsites, OS); 527*0b57cec5SDimitry Andric for (const auto &J : S.getCallsiteSamples()) 528*0b57cec5SDimitry Andric for (const auto &FS : J.second) { 529*0b57cec5SDimitry Andric LineLocation Loc = J.first; 530*0b57cec5SDimitry Andric const FunctionSamples &CalleeSamples = FS.second; 531*0b57cec5SDimitry Andric encodeULEB128(Loc.LineOffset, OS); 532*0b57cec5SDimitry Andric encodeULEB128(Loc.Discriminator, OS); 533*0b57cec5SDimitry Andric if (std::error_code EC = writeBody(CalleeSamples)) 534*0b57cec5SDimitry Andric return EC; 535*0b57cec5SDimitry Andric } 536*0b57cec5SDimitry Andric 537*0b57cec5SDimitry Andric return sampleprof_error::success; 538*0b57cec5SDimitry Andric } 539*0b57cec5SDimitry Andric 540*0b57cec5SDimitry Andric /// Write samples of a top-level function to a binary file. 541*0b57cec5SDimitry Andric /// 542*0b57cec5SDimitry Andric /// \returns true if the samples were written successfully, false otherwise. 5438bcb0991SDimitry Andric std::error_code 5448bcb0991SDimitry Andric SampleProfileWriterBinary::writeSample(const FunctionSamples &S) { 545*0b57cec5SDimitry Andric encodeULEB128(S.getHeadSamples(), *OutputStream); 546*0b57cec5SDimitry Andric return writeBody(S); 547*0b57cec5SDimitry Andric } 548*0b57cec5SDimitry Andric 549*0b57cec5SDimitry Andric std::error_code 5508bcb0991SDimitry Andric SampleProfileWriterCompactBinary::writeSample(const FunctionSamples &S) { 551*0b57cec5SDimitry Andric uint64_t Offset = OutputStream->tell(); 552*0b57cec5SDimitry Andric StringRef Name = S.getName(); 553*0b57cec5SDimitry Andric FuncOffsetTable[Name] = Offset; 554*0b57cec5SDimitry Andric encodeULEB128(S.getHeadSamples(), *OutputStream); 555*0b57cec5SDimitry Andric return writeBody(S); 556*0b57cec5SDimitry Andric } 557*0b57cec5SDimitry Andric 558*0b57cec5SDimitry Andric /// Create a sample profile file writer based on the specified format. 559*0b57cec5SDimitry Andric /// 560*0b57cec5SDimitry Andric /// \param Filename The file to create. 561*0b57cec5SDimitry Andric /// 562*0b57cec5SDimitry Andric /// \param Format Encoding format for the profile file. 563*0b57cec5SDimitry Andric /// 564*0b57cec5SDimitry Andric /// \returns an error code indicating the status of the created writer. 565*0b57cec5SDimitry Andric ErrorOr<std::unique_ptr<SampleProfileWriter>> 566*0b57cec5SDimitry Andric SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) { 567*0b57cec5SDimitry Andric std::error_code EC; 568*0b57cec5SDimitry Andric std::unique_ptr<raw_ostream> OS; 5698bcb0991SDimitry Andric if (Format == SPF_Binary || Format == SPF_Ext_Binary || 5708bcb0991SDimitry Andric Format == SPF_Compact_Binary) 5718bcb0991SDimitry Andric OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_None)); 572*0b57cec5SDimitry Andric else 5738bcb0991SDimitry Andric OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_Text)); 574*0b57cec5SDimitry Andric if (EC) 575*0b57cec5SDimitry Andric return EC; 576*0b57cec5SDimitry Andric 577*0b57cec5SDimitry Andric return create(OS, Format); 578*0b57cec5SDimitry Andric } 579*0b57cec5SDimitry Andric 580*0b57cec5SDimitry Andric /// Create a sample profile stream writer based on the specified format. 581*0b57cec5SDimitry Andric /// 582*0b57cec5SDimitry Andric /// \param OS The output stream to store the profile data to. 583*0b57cec5SDimitry Andric /// 584*0b57cec5SDimitry Andric /// \param Format Encoding format for the profile file. 585*0b57cec5SDimitry Andric /// 586*0b57cec5SDimitry Andric /// \returns an error code indicating the status of the created writer. 587*0b57cec5SDimitry Andric ErrorOr<std::unique_ptr<SampleProfileWriter>> 588*0b57cec5SDimitry Andric SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS, 589*0b57cec5SDimitry Andric SampleProfileFormat Format) { 590*0b57cec5SDimitry Andric std::error_code EC; 591*0b57cec5SDimitry Andric std::unique_ptr<SampleProfileWriter> Writer; 592*0b57cec5SDimitry Andric 593*0b57cec5SDimitry Andric if (Format == SPF_Binary) 594*0b57cec5SDimitry Andric Writer.reset(new SampleProfileWriterRawBinary(OS)); 5958bcb0991SDimitry Andric else if (Format == SPF_Ext_Binary) 5968bcb0991SDimitry Andric Writer.reset(new SampleProfileWriterExtBinary(OS)); 597*0b57cec5SDimitry Andric else if (Format == SPF_Compact_Binary) 598*0b57cec5SDimitry Andric Writer.reset(new SampleProfileWriterCompactBinary(OS)); 599*0b57cec5SDimitry Andric else if (Format == SPF_Text) 600*0b57cec5SDimitry Andric Writer.reset(new SampleProfileWriterText(OS)); 601*0b57cec5SDimitry Andric else if (Format == SPF_GCC) 602*0b57cec5SDimitry Andric EC = sampleprof_error::unsupported_writing_format; 603*0b57cec5SDimitry Andric else 604*0b57cec5SDimitry Andric EC = sampleprof_error::unrecognized_format; 605*0b57cec5SDimitry Andric 606*0b57cec5SDimitry Andric if (EC) 607*0b57cec5SDimitry Andric return EC; 608*0b57cec5SDimitry Andric 6098bcb0991SDimitry Andric Writer->Format = Format; 610*0b57cec5SDimitry Andric return std::move(Writer); 611*0b57cec5SDimitry Andric } 612*0b57cec5SDimitry Andric 613*0b57cec5SDimitry Andric void SampleProfileWriter::computeSummary( 614*0b57cec5SDimitry Andric const StringMap<FunctionSamples> &ProfileMap) { 615*0b57cec5SDimitry Andric SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs); 616*0b57cec5SDimitry Andric for (const auto &I : ProfileMap) { 617*0b57cec5SDimitry Andric const FunctionSamples &Profile = I.second; 618*0b57cec5SDimitry Andric Builder.addRecord(Profile); 619*0b57cec5SDimitry Andric } 620*0b57cec5SDimitry Andric Summary = Builder.getSummary(); 621*0b57cec5SDimitry Andric } 622