xref: /freebsd/contrib/llvm-project/llvm/lib/ProfileData/InstrProfWriter.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
10b57cec5SDimitry Andric //===- InstrProfWriter.cpp - Instrumented profiling writer ----------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file contains support for writing profiling data for clang's
100b57cec5SDimitry Andric // instrumentation based PGO and coverage.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "llvm/ProfileData/InstrProfWriter.h"
150b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h"
1606c3fb27SDimitry Andric #include "llvm/ADT/SetVector.h"
170b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
180b57cec5SDimitry Andric #include "llvm/IR/ProfileSummary.h"
190b57cec5SDimitry Andric #include "llvm/ProfileData/InstrProf.h"
2081ad6265SDimitry Andric #include "llvm/ProfileData/MemProf.h"
210b57cec5SDimitry Andric #include "llvm/ProfileData/ProfileCommon.h"
22*0fca6ea1SDimitry Andric #include "llvm/Support/Compression.h"
230b57cec5SDimitry Andric #include "llvm/Support/Endian.h"
240b57cec5SDimitry Andric #include "llvm/Support/EndianStream.h"
250b57cec5SDimitry Andric #include "llvm/Support/Error.h"
26*0fca6ea1SDimitry Andric #include "llvm/Support/FormatVariadic.h"
270b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
280b57cec5SDimitry Andric #include "llvm/Support/OnDiskHashTable.h"
290b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
300b57cec5SDimitry Andric #include <cstdint>
310b57cec5SDimitry Andric #include <memory>
320b57cec5SDimitry Andric #include <string>
330b57cec5SDimitry Andric #include <tuple>
340b57cec5SDimitry Andric #include <utility>
350b57cec5SDimitry Andric #include <vector>
360b57cec5SDimitry Andric 
370b57cec5SDimitry Andric using namespace llvm;
380b57cec5SDimitry Andric 
390b57cec5SDimitry Andric // A struct to define how the data stream should be patched. For Indexed
400b57cec5SDimitry Andric // profiling, only uint64_t data type is needed.
410b57cec5SDimitry Andric struct PatchItem {
420b57cec5SDimitry Andric   uint64_t Pos;         // Where to patch.
43*0fca6ea1SDimitry Andric   ArrayRef<uint64_t> D; // An array of source data.
440b57cec5SDimitry Andric };
450b57cec5SDimitry Andric 
460b57cec5SDimitry Andric namespace llvm {
470b57cec5SDimitry Andric 
480b57cec5SDimitry Andric // A wrapper class to abstract writer stream with support of bytes
490b57cec5SDimitry Andric // back patching.
500b57cec5SDimitry Andric class ProfOStream {
510b57cec5SDimitry Andric public:
ProfOStream(raw_fd_ostream & FD)520b57cec5SDimitry Andric   ProfOStream(raw_fd_ostream &FD)
535f757f3fSDimitry Andric       : IsFDOStream(true), OS(FD), LE(FD, llvm::endianness::little) {}
ProfOStream(raw_string_ostream & STR)540b57cec5SDimitry Andric   ProfOStream(raw_string_ostream &STR)
555f757f3fSDimitry Andric       : IsFDOStream(false), OS(STR), LE(STR, llvm::endianness::little) {}
560b57cec5SDimitry Andric 
tell() const57*0fca6ea1SDimitry Andric   [[nodiscard]] uint64_t tell() const { return OS.tell(); }
write(uint64_t V)580b57cec5SDimitry Andric   void write(uint64_t V) { LE.write<uint64_t>(V); }
write32(uint32_t V)59*0fca6ea1SDimitry Andric   void write32(uint32_t V) { LE.write<uint32_t>(V); }
writeByte(uint8_t V)60bdd1243dSDimitry Andric   void writeByte(uint8_t V) { LE.write<uint8_t>(V); }
610b57cec5SDimitry Andric 
620b57cec5SDimitry Andric   // \c patch can only be called when all data is written and flushed.
630b57cec5SDimitry Andric   // For raw_string_ostream, the patch is done on the target string
640b57cec5SDimitry Andric   // directly and it won't be reflected in the stream's internal buffer.
patch(ArrayRef<PatchItem> P)65*0fca6ea1SDimitry Andric   void patch(ArrayRef<PatchItem> P) {
660b57cec5SDimitry Andric     using namespace support;
670b57cec5SDimitry Andric 
680b57cec5SDimitry Andric     if (IsFDOStream) {
690b57cec5SDimitry Andric       raw_fd_ostream &FDOStream = static_cast<raw_fd_ostream &>(OS);
7081ad6265SDimitry Andric       const uint64_t LastPos = FDOStream.tell();
71*0fca6ea1SDimitry Andric       for (const auto &K : P) {
72*0fca6ea1SDimitry Andric         FDOStream.seek(K.Pos);
73*0fca6ea1SDimitry Andric         for (uint64_t Elem : K.D)
74*0fca6ea1SDimitry Andric           write(Elem);
750b57cec5SDimitry Andric       }
7681ad6265SDimitry Andric       // Reset the stream to the last position after patching so that users
7781ad6265SDimitry Andric       // don't accidentally overwrite data. This makes it consistent with
7881ad6265SDimitry Andric       // the string stream below which replaces the data directly.
7981ad6265SDimitry Andric       FDOStream.seek(LastPos);
800b57cec5SDimitry Andric     } else {
810b57cec5SDimitry Andric       raw_string_ostream &SOStream = static_cast<raw_string_ostream &>(OS);
820b57cec5SDimitry Andric       std::string &Data = SOStream.str(); // with flush
83*0fca6ea1SDimitry Andric       for (const auto &K : P) {
84*0fca6ea1SDimitry Andric         for (int I = 0, E = K.D.size(); I != E; I++) {
855f757f3fSDimitry Andric           uint64_t Bytes =
86*0fca6ea1SDimitry Andric               endian::byte_swap<uint64_t, llvm::endianness::little>(K.D[I]);
87*0fca6ea1SDimitry Andric           Data.replace(K.Pos + I * sizeof(uint64_t), sizeof(uint64_t),
880b57cec5SDimitry Andric                        (const char *)&Bytes, sizeof(uint64_t));
890b57cec5SDimitry Andric         }
900b57cec5SDimitry Andric       }
910b57cec5SDimitry Andric     }
920b57cec5SDimitry Andric   }
930b57cec5SDimitry Andric 
940b57cec5SDimitry Andric   // If \c OS is an instance of \c raw_fd_ostream, this field will be
950b57cec5SDimitry Andric   // true. Otherwise, \c OS will be an raw_string_ostream.
960b57cec5SDimitry Andric   bool IsFDOStream;
970b57cec5SDimitry Andric   raw_ostream &OS;
980b57cec5SDimitry Andric   support::endian::Writer LE;
990b57cec5SDimitry Andric };
1000b57cec5SDimitry Andric 
1010b57cec5SDimitry Andric class InstrProfRecordWriterTrait {
1020b57cec5SDimitry Andric public:
1030b57cec5SDimitry Andric   using key_type = StringRef;
1040b57cec5SDimitry Andric   using key_type_ref = StringRef;
1050b57cec5SDimitry Andric 
1060b57cec5SDimitry Andric   using data_type = const InstrProfWriter::ProfilingData *const;
1070b57cec5SDimitry Andric   using data_type_ref = const InstrProfWriter::ProfilingData *const;
1080b57cec5SDimitry Andric 
1090b57cec5SDimitry Andric   using hash_value_type = uint64_t;
1100b57cec5SDimitry Andric   using offset_type = uint64_t;
1110b57cec5SDimitry Andric 
1125f757f3fSDimitry Andric   llvm::endianness ValueProfDataEndianness = llvm::endianness::little;
1130b57cec5SDimitry Andric   InstrProfSummaryBuilder *SummaryBuilder;
1140b57cec5SDimitry Andric   InstrProfSummaryBuilder *CSSummaryBuilder;
1150b57cec5SDimitry Andric 
1160b57cec5SDimitry Andric   InstrProfRecordWriterTrait() = default;
1170b57cec5SDimitry Andric 
ComputeHash(key_type_ref K)1180b57cec5SDimitry Andric   static hash_value_type ComputeHash(key_type_ref K) {
1190b57cec5SDimitry Andric     return IndexedInstrProf::ComputeHash(K);
1200b57cec5SDimitry Andric   }
1210b57cec5SDimitry Andric 
1220b57cec5SDimitry Andric   static std::pair<offset_type, offset_type>
EmitKeyDataLength(raw_ostream & Out,key_type_ref K,data_type_ref V)1230b57cec5SDimitry Andric   EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) {
1240b57cec5SDimitry Andric     using namespace support;
1250b57cec5SDimitry Andric 
1265f757f3fSDimitry Andric     endian::Writer LE(Out, llvm::endianness::little);
1270b57cec5SDimitry Andric 
1280b57cec5SDimitry Andric     offset_type N = K.size();
1290b57cec5SDimitry Andric     LE.write<offset_type>(N);
1300b57cec5SDimitry Andric 
1310b57cec5SDimitry Andric     offset_type M = 0;
1320b57cec5SDimitry Andric     for (const auto &ProfileData : *V) {
1330b57cec5SDimitry Andric       const InstrProfRecord &ProfRecord = ProfileData.second;
1340b57cec5SDimitry Andric       M += sizeof(uint64_t); // The function hash
1350b57cec5SDimitry Andric       M += sizeof(uint64_t); // The size of the Counts vector
1360b57cec5SDimitry Andric       M += ProfRecord.Counts.size() * sizeof(uint64_t);
1375f757f3fSDimitry Andric       M += sizeof(uint64_t); // The size of the Bitmap vector
1385f757f3fSDimitry Andric       M += ProfRecord.BitmapBytes.size() * sizeof(uint64_t);
1390b57cec5SDimitry Andric 
1400b57cec5SDimitry Andric       // Value data
1410b57cec5SDimitry Andric       M += ValueProfData::getSize(ProfileData.second);
1420b57cec5SDimitry Andric     }
1430b57cec5SDimitry Andric     LE.write<offset_type>(M);
1440b57cec5SDimitry Andric 
1450b57cec5SDimitry Andric     return std::make_pair(N, M);
1460b57cec5SDimitry Andric   }
1470b57cec5SDimitry Andric 
EmitKey(raw_ostream & Out,key_type_ref K,offset_type N)1480b57cec5SDimitry Andric   void EmitKey(raw_ostream &Out, key_type_ref K, offset_type N) {
1490b57cec5SDimitry Andric     Out.write(K.data(), N);
1500b57cec5SDimitry Andric   }
1510b57cec5SDimitry Andric 
EmitData(raw_ostream & Out,key_type_ref,data_type_ref V,offset_type)1520b57cec5SDimitry Andric   void EmitData(raw_ostream &Out, key_type_ref, data_type_ref V, offset_type) {
1530b57cec5SDimitry Andric     using namespace support;
1540b57cec5SDimitry Andric 
1555f757f3fSDimitry Andric     endian::Writer LE(Out, llvm::endianness::little);
1560b57cec5SDimitry Andric     for (const auto &ProfileData : *V) {
1570b57cec5SDimitry Andric       const InstrProfRecord &ProfRecord = ProfileData.second;
1580b57cec5SDimitry Andric       if (NamedInstrProfRecord::hasCSFlagInHash(ProfileData.first))
1590b57cec5SDimitry Andric         CSSummaryBuilder->addRecord(ProfRecord);
1600b57cec5SDimitry Andric       else
1610b57cec5SDimitry Andric         SummaryBuilder->addRecord(ProfRecord);
1620b57cec5SDimitry Andric 
1630b57cec5SDimitry Andric       LE.write<uint64_t>(ProfileData.first); // Function hash
1640b57cec5SDimitry Andric       LE.write<uint64_t>(ProfRecord.Counts.size());
1650b57cec5SDimitry Andric       for (uint64_t I : ProfRecord.Counts)
1660b57cec5SDimitry Andric         LE.write<uint64_t>(I);
1670b57cec5SDimitry Andric 
1685f757f3fSDimitry Andric       LE.write<uint64_t>(ProfRecord.BitmapBytes.size());
1695f757f3fSDimitry Andric       for (uint64_t I : ProfRecord.BitmapBytes)
1705f757f3fSDimitry Andric         LE.write<uint64_t>(I);
1715f757f3fSDimitry Andric 
1720b57cec5SDimitry Andric       // Write value data
1730b57cec5SDimitry Andric       std::unique_ptr<ValueProfData> VDataPtr =
1740b57cec5SDimitry Andric           ValueProfData::serializeFrom(ProfileData.second);
1750b57cec5SDimitry Andric       uint32_t S = VDataPtr->getSize();
1760b57cec5SDimitry Andric       VDataPtr->swapBytesFromHost(ValueProfDataEndianness);
1770b57cec5SDimitry Andric       Out.write((const char *)VDataPtr.get(), S);
1780b57cec5SDimitry Andric     }
1790b57cec5SDimitry Andric   }
1800b57cec5SDimitry Andric };
1810b57cec5SDimitry Andric 
1820b57cec5SDimitry Andric } // end namespace llvm
1830b57cec5SDimitry Andric 
InstrProfWriter(bool Sparse,uint64_t TemporalProfTraceReservoirSize,uint64_t MaxTemporalProfTraceLength,bool WritePrevVersion,memprof::IndexedVersion MemProfVersionRequested,bool MemProfFullSchema)184*0fca6ea1SDimitry Andric InstrProfWriter::InstrProfWriter(
185*0fca6ea1SDimitry Andric     bool Sparse, uint64_t TemporalProfTraceReservoirSize,
186*0fca6ea1SDimitry Andric     uint64_t MaxTemporalProfTraceLength, bool WritePrevVersion,
187*0fca6ea1SDimitry Andric     memprof::IndexedVersion MemProfVersionRequested, bool MemProfFullSchema)
18806c3fb27SDimitry Andric     : Sparse(Sparse), MaxTemporalProfTraceLength(MaxTemporalProfTraceLength),
18906c3fb27SDimitry Andric       TemporalProfTraceReservoirSize(TemporalProfTraceReservoirSize),
190*0fca6ea1SDimitry Andric       InfoObj(new InstrProfRecordWriterTrait()),
191*0fca6ea1SDimitry Andric       WritePrevVersion(WritePrevVersion),
192*0fca6ea1SDimitry Andric       MemProfVersionRequested(MemProfVersionRequested),
193*0fca6ea1SDimitry Andric       MemProfFullSchema(MemProfFullSchema) {}
1940b57cec5SDimitry Andric 
~InstrProfWriter()1950b57cec5SDimitry Andric InstrProfWriter::~InstrProfWriter() { delete InfoObj; }
1960b57cec5SDimitry Andric 
1970b57cec5SDimitry Andric // Internal interface for testing purpose only.
setValueProfDataEndianness(llvm::endianness Endianness)1985f757f3fSDimitry Andric void InstrProfWriter::setValueProfDataEndianness(llvm::endianness Endianness) {
1990b57cec5SDimitry Andric   InfoObj->ValueProfDataEndianness = Endianness;
2000b57cec5SDimitry Andric }
2010b57cec5SDimitry Andric 
setOutputSparse(bool Sparse)2020b57cec5SDimitry Andric void InstrProfWriter::setOutputSparse(bool Sparse) {
2030b57cec5SDimitry Andric   this->Sparse = Sparse;
2040b57cec5SDimitry Andric }
2050b57cec5SDimitry Andric 
addRecord(NamedInstrProfRecord && I,uint64_t Weight,function_ref<void (Error)> Warn)2060b57cec5SDimitry Andric void InstrProfWriter::addRecord(NamedInstrProfRecord &&I, uint64_t Weight,
2070b57cec5SDimitry Andric                                 function_ref<void(Error)> Warn) {
2080b57cec5SDimitry Andric   auto Name = I.Name;
2090b57cec5SDimitry Andric   auto Hash = I.Hash;
2100b57cec5SDimitry Andric   addRecord(Name, Hash, std::move(I), Weight, Warn);
2110b57cec5SDimitry Andric }
2120b57cec5SDimitry Andric 
overlapRecord(NamedInstrProfRecord && Other,OverlapStats & Overlap,OverlapStats & FuncLevelOverlap,const OverlapFuncFilters & FuncFilter)2130b57cec5SDimitry Andric void InstrProfWriter::overlapRecord(NamedInstrProfRecord &&Other,
2140b57cec5SDimitry Andric                                     OverlapStats &Overlap,
2150b57cec5SDimitry Andric                                     OverlapStats &FuncLevelOverlap,
2160b57cec5SDimitry Andric                                     const OverlapFuncFilters &FuncFilter) {
2170b57cec5SDimitry Andric   auto Name = Other.Name;
2180b57cec5SDimitry Andric   auto Hash = Other.Hash;
2198bcb0991SDimitry Andric   Other.accumulateCounts(FuncLevelOverlap.Test);
22006c3fb27SDimitry Andric   if (!FunctionData.contains(Name)) {
2210b57cec5SDimitry Andric     Overlap.addOneUnique(FuncLevelOverlap.Test);
2220b57cec5SDimitry Andric     return;
2230b57cec5SDimitry Andric   }
2240b57cec5SDimitry Andric   if (FuncLevelOverlap.Test.CountSum < 1.0f) {
2250b57cec5SDimitry Andric     Overlap.Overlap.NumEntries += 1;
2260b57cec5SDimitry Andric     return;
2270b57cec5SDimitry Andric   }
2280b57cec5SDimitry Andric   auto &ProfileDataMap = FunctionData[Name];
2290b57cec5SDimitry Andric   bool NewFunc;
2300b57cec5SDimitry Andric   ProfilingData::iterator Where;
2310b57cec5SDimitry Andric   std::tie(Where, NewFunc) =
2320b57cec5SDimitry Andric       ProfileDataMap.insert(std::make_pair(Hash, InstrProfRecord()));
2330b57cec5SDimitry Andric   if (NewFunc) {
2340b57cec5SDimitry Andric     Overlap.addOneMismatch(FuncLevelOverlap.Test);
2350b57cec5SDimitry Andric     return;
2360b57cec5SDimitry Andric   }
2370b57cec5SDimitry Andric   InstrProfRecord &Dest = Where->second;
2380b57cec5SDimitry Andric 
2390b57cec5SDimitry Andric   uint64_t ValueCutoff = FuncFilter.ValueCutoff;
240349cc55cSDimitry Andric   if (!FuncFilter.NameFilter.empty() && Name.contains(FuncFilter.NameFilter))
2410b57cec5SDimitry Andric     ValueCutoff = 0;
2420b57cec5SDimitry Andric 
2430b57cec5SDimitry Andric   Dest.overlap(Other, Overlap, FuncLevelOverlap, ValueCutoff);
2440b57cec5SDimitry Andric }
2450b57cec5SDimitry Andric 
addRecord(StringRef Name,uint64_t Hash,InstrProfRecord && I,uint64_t Weight,function_ref<void (Error)> Warn)2460b57cec5SDimitry Andric void InstrProfWriter::addRecord(StringRef Name, uint64_t Hash,
2470b57cec5SDimitry Andric                                 InstrProfRecord &&I, uint64_t Weight,
2480b57cec5SDimitry Andric                                 function_ref<void(Error)> Warn) {
2490b57cec5SDimitry Andric   auto &ProfileDataMap = FunctionData[Name];
2500b57cec5SDimitry Andric 
2510b57cec5SDimitry Andric   bool NewFunc;
2520b57cec5SDimitry Andric   ProfilingData::iterator Where;
2530b57cec5SDimitry Andric   std::tie(Where, NewFunc) =
2540b57cec5SDimitry Andric       ProfileDataMap.insert(std::make_pair(Hash, InstrProfRecord()));
2550b57cec5SDimitry Andric   InstrProfRecord &Dest = Where->second;
2560b57cec5SDimitry Andric 
2570b57cec5SDimitry Andric   auto MapWarn = [&](instrprof_error E) {
2580b57cec5SDimitry Andric     Warn(make_error<InstrProfError>(E));
2590b57cec5SDimitry Andric   };
2600b57cec5SDimitry Andric 
2610b57cec5SDimitry Andric   if (NewFunc) {
2620b57cec5SDimitry Andric     // We've never seen a function with this name and hash, add it.
2630b57cec5SDimitry Andric     Dest = std::move(I);
2640b57cec5SDimitry Andric     if (Weight > 1)
265e8d8bef9SDimitry Andric       Dest.scale(Weight, 1, MapWarn);
2660b57cec5SDimitry Andric   } else {
2670b57cec5SDimitry Andric     // We're updating a function we've seen before.
2680b57cec5SDimitry Andric     Dest.merge(I, Weight, MapWarn);
2690b57cec5SDimitry Andric   }
2700b57cec5SDimitry Andric 
2710b57cec5SDimitry Andric   Dest.sortValueData();
2720b57cec5SDimitry Andric }
2730b57cec5SDimitry Andric 
addMemProfRecord(const Function::GUID Id,const memprof::IndexedMemProfRecord & Record)27481ad6265SDimitry Andric void InstrProfWriter::addMemProfRecord(
27581ad6265SDimitry Andric     const Function::GUID Id, const memprof::IndexedMemProfRecord &Record) {
276*0fca6ea1SDimitry Andric   auto [Iter, Inserted] = MemProfData.Records.insert({Id, Record});
27781ad6265SDimitry Andric   // If we inserted a new record then we are done.
278*0fca6ea1SDimitry Andric   if (Inserted) {
27981ad6265SDimitry Andric     return;
28081ad6265SDimitry Andric   }
281*0fca6ea1SDimitry Andric   memprof::IndexedMemProfRecord &Existing = Iter->second;
28281ad6265SDimitry Andric   Existing.merge(Record);
28381ad6265SDimitry Andric }
28481ad6265SDimitry Andric 
addMemProfFrame(const memprof::FrameId Id,const memprof::Frame & Frame,function_ref<void (Error)> Warn)28581ad6265SDimitry Andric bool InstrProfWriter::addMemProfFrame(const memprof::FrameId Id,
28681ad6265SDimitry Andric                                       const memprof::Frame &Frame,
28781ad6265SDimitry Andric                                       function_ref<void(Error)> Warn) {
288*0fca6ea1SDimitry Andric   auto [Iter, Inserted] = MemProfData.Frames.insert({Id, Frame});
28981ad6265SDimitry Andric   // If a mapping already exists for the current frame id and it does not
29081ad6265SDimitry Andric   // match the new mapping provided then reset the existing contents and bail
29181ad6265SDimitry Andric   // out. We don't support the merging of memprof data whose Frame -> Id
29281ad6265SDimitry Andric   // mapping across profiles is inconsistent.
293*0fca6ea1SDimitry Andric   if (!Inserted && Iter->second != Frame) {
29481ad6265SDimitry Andric     Warn(make_error<InstrProfError>(instrprof_error::malformed,
29581ad6265SDimitry Andric                                     "frame to id mapping mismatch"));
29681ad6265SDimitry Andric     return false;
29781ad6265SDimitry Andric   }
29881ad6265SDimitry Andric   return true;
29981ad6265SDimitry Andric }
30081ad6265SDimitry Andric 
addMemProfCallStack(const memprof::CallStackId CSId,const llvm::SmallVector<memprof::FrameId> & CallStack,function_ref<void (Error)> Warn)301*0fca6ea1SDimitry Andric bool InstrProfWriter::addMemProfCallStack(
302*0fca6ea1SDimitry Andric     const memprof::CallStackId CSId,
303*0fca6ea1SDimitry Andric     const llvm::SmallVector<memprof::FrameId> &CallStack,
304*0fca6ea1SDimitry Andric     function_ref<void(Error)> Warn) {
305*0fca6ea1SDimitry Andric   auto [Iter, Inserted] = MemProfData.CallStacks.insert({CSId, CallStack});
306*0fca6ea1SDimitry Andric   // If a mapping already exists for the current call stack id and it does not
307*0fca6ea1SDimitry Andric   // match the new mapping provided then reset the existing contents and bail
308*0fca6ea1SDimitry Andric   // out. We don't support the merging of memprof data whose CallStack -> Id
309*0fca6ea1SDimitry Andric   // mapping across profiles is inconsistent.
310*0fca6ea1SDimitry Andric   if (!Inserted && Iter->second != CallStack) {
311*0fca6ea1SDimitry Andric     Warn(make_error<InstrProfError>(instrprof_error::malformed,
312*0fca6ea1SDimitry Andric                                     "call stack to id mapping mismatch"));
313*0fca6ea1SDimitry Andric     return false;
314*0fca6ea1SDimitry Andric   }
315*0fca6ea1SDimitry Andric   return true;
316*0fca6ea1SDimitry Andric }
317*0fca6ea1SDimitry Andric 
addBinaryIds(ArrayRef<llvm::object::BuildID> BIs)318bdd1243dSDimitry Andric void InstrProfWriter::addBinaryIds(ArrayRef<llvm::object::BuildID> BIs) {
319bdd1243dSDimitry Andric   llvm::append_range(BinaryIds, BIs);
320bdd1243dSDimitry Andric }
321bdd1243dSDimitry Andric 
addTemporalProfileTrace(TemporalProfTraceTy Trace)32206c3fb27SDimitry Andric void InstrProfWriter::addTemporalProfileTrace(TemporalProfTraceTy Trace) {
323*0fca6ea1SDimitry Andric   assert(Trace.FunctionNameRefs.size() <= MaxTemporalProfTraceLength);
324*0fca6ea1SDimitry Andric   assert(!Trace.FunctionNameRefs.empty());
32506c3fb27SDimitry Andric   if (TemporalProfTraceStreamSize < TemporalProfTraceReservoirSize) {
32606c3fb27SDimitry Andric     // Simply append the trace if we have not yet hit our reservoir size limit.
32706c3fb27SDimitry Andric     TemporalProfTraces.push_back(std::move(Trace));
32806c3fb27SDimitry Andric   } else {
32906c3fb27SDimitry Andric     // Otherwise, replace a random trace in the stream.
33006c3fb27SDimitry Andric     std::uniform_int_distribution<uint64_t> Distribution(
33106c3fb27SDimitry Andric         0, TemporalProfTraceStreamSize);
33206c3fb27SDimitry Andric     uint64_t RandomIndex = Distribution(RNG);
33306c3fb27SDimitry Andric     if (RandomIndex < TemporalProfTraces.size())
33406c3fb27SDimitry Andric       TemporalProfTraces[RandomIndex] = std::move(Trace);
33506c3fb27SDimitry Andric   }
33606c3fb27SDimitry Andric   ++TemporalProfTraceStreamSize;
33706c3fb27SDimitry Andric }
33806c3fb27SDimitry Andric 
addTemporalProfileTraces(SmallVectorImpl<TemporalProfTraceTy> & SrcTraces,uint64_t SrcStreamSize)33906c3fb27SDimitry Andric void InstrProfWriter::addTemporalProfileTraces(
34006c3fb27SDimitry Andric     SmallVectorImpl<TemporalProfTraceTy> &SrcTraces, uint64_t SrcStreamSize) {
341*0fca6ea1SDimitry Andric   for (auto &Trace : SrcTraces)
342*0fca6ea1SDimitry Andric     if (Trace.FunctionNameRefs.size() > MaxTemporalProfTraceLength)
343*0fca6ea1SDimitry Andric       Trace.FunctionNameRefs.resize(MaxTemporalProfTraceLength);
344*0fca6ea1SDimitry Andric   llvm::erase_if(SrcTraces, [](auto &T) { return T.FunctionNameRefs.empty(); });
34506c3fb27SDimitry Andric   // Assume that the source has the same reservoir size as the destination to
34606c3fb27SDimitry Andric   // avoid needing to record it in the indexed profile format.
34706c3fb27SDimitry Andric   bool IsDestSampled =
34806c3fb27SDimitry Andric       (TemporalProfTraceStreamSize > TemporalProfTraceReservoirSize);
34906c3fb27SDimitry Andric   bool IsSrcSampled = (SrcStreamSize > TemporalProfTraceReservoirSize);
35006c3fb27SDimitry Andric   if (!IsDestSampled && IsSrcSampled) {
35106c3fb27SDimitry Andric     // If one of the traces are sampled, ensure that it belongs to Dest.
35206c3fb27SDimitry Andric     std::swap(TemporalProfTraces, SrcTraces);
35306c3fb27SDimitry Andric     std::swap(TemporalProfTraceStreamSize, SrcStreamSize);
35406c3fb27SDimitry Andric     std::swap(IsDestSampled, IsSrcSampled);
35506c3fb27SDimitry Andric   }
35606c3fb27SDimitry Andric   if (!IsSrcSampled) {
35706c3fb27SDimitry Andric     // If the source stream is not sampled, we add each source trace normally.
35806c3fb27SDimitry Andric     for (auto &Trace : SrcTraces)
35906c3fb27SDimitry Andric       addTemporalProfileTrace(std::move(Trace));
36006c3fb27SDimitry Andric     return;
36106c3fb27SDimitry Andric   }
36206c3fb27SDimitry Andric   // Otherwise, we find the traces that would have been removed if we added
36306c3fb27SDimitry Andric   // the whole source stream.
36406c3fb27SDimitry Andric   SmallSetVector<uint64_t, 8> IndicesToReplace;
36506c3fb27SDimitry Andric   for (uint64_t I = 0; I < SrcStreamSize; I++) {
36606c3fb27SDimitry Andric     std::uniform_int_distribution<uint64_t> Distribution(
36706c3fb27SDimitry Andric         0, TemporalProfTraceStreamSize);
36806c3fb27SDimitry Andric     uint64_t RandomIndex = Distribution(RNG);
36906c3fb27SDimitry Andric     if (RandomIndex < TemporalProfTraces.size())
37006c3fb27SDimitry Andric       IndicesToReplace.insert(RandomIndex);
37106c3fb27SDimitry Andric     ++TemporalProfTraceStreamSize;
37206c3fb27SDimitry Andric   }
37306c3fb27SDimitry Andric   // Then we insert a random sample of the source traces.
37406c3fb27SDimitry Andric   llvm::shuffle(SrcTraces.begin(), SrcTraces.end(), RNG);
37506c3fb27SDimitry Andric   for (const auto &[Index, Trace] : llvm::zip(IndicesToReplace, SrcTraces))
37606c3fb27SDimitry Andric     TemporalProfTraces[Index] = std::move(Trace);
37706c3fb27SDimitry Andric }
37806c3fb27SDimitry Andric 
mergeRecordsFromWriter(InstrProfWriter && IPW,function_ref<void (Error)> Warn)3790b57cec5SDimitry Andric void InstrProfWriter::mergeRecordsFromWriter(InstrProfWriter &&IPW,
3800b57cec5SDimitry Andric                                              function_ref<void(Error)> Warn) {
3810b57cec5SDimitry Andric   for (auto &I : IPW.FunctionData)
3820b57cec5SDimitry Andric     for (auto &Func : I.getValue())
3830b57cec5SDimitry Andric       addRecord(I.getKey(), Func.first, std::move(Func.second), 1, Warn);
38481ad6265SDimitry Andric 
385bdd1243dSDimitry Andric   BinaryIds.reserve(BinaryIds.size() + IPW.BinaryIds.size());
386bdd1243dSDimitry Andric   for (auto &I : IPW.BinaryIds)
387bdd1243dSDimitry Andric     addBinaryIds(I);
388bdd1243dSDimitry Andric 
38906c3fb27SDimitry Andric   addTemporalProfileTraces(IPW.TemporalProfTraces,
39006c3fb27SDimitry Andric                            IPW.TemporalProfTraceStreamSize);
39106c3fb27SDimitry Andric 
392*0fca6ea1SDimitry Andric   MemProfData.Frames.reserve(IPW.MemProfData.Frames.size());
393*0fca6ea1SDimitry Andric   for (auto &[FrameId, Frame] : IPW.MemProfData.Frames) {
39481ad6265SDimitry Andric     // If we weren't able to add the frame mappings then it doesn't make sense
39581ad6265SDimitry Andric     // to try to merge the records from this profile.
396*0fca6ea1SDimitry Andric     if (!addMemProfFrame(FrameId, Frame, Warn))
39781ad6265SDimitry Andric       return;
39881ad6265SDimitry Andric   }
39981ad6265SDimitry Andric 
400*0fca6ea1SDimitry Andric   MemProfData.CallStacks.reserve(IPW.MemProfData.CallStacks.size());
401*0fca6ea1SDimitry Andric   for (auto &[CSId, CallStack] : IPW.MemProfData.CallStacks) {
402*0fca6ea1SDimitry Andric     if (!addMemProfCallStack(CSId, CallStack, Warn))
403*0fca6ea1SDimitry Andric       return;
404*0fca6ea1SDimitry Andric   }
405*0fca6ea1SDimitry Andric 
406*0fca6ea1SDimitry Andric   MemProfData.Records.reserve(IPW.MemProfData.Records.size());
407*0fca6ea1SDimitry Andric   for (auto &[GUID, Record] : IPW.MemProfData.Records) {
408*0fca6ea1SDimitry Andric     addMemProfRecord(GUID, Record);
40981ad6265SDimitry Andric   }
4100b57cec5SDimitry Andric }
4110b57cec5SDimitry Andric 
shouldEncodeData(const ProfilingData & PD)4120b57cec5SDimitry Andric bool InstrProfWriter::shouldEncodeData(const ProfilingData &PD) {
4130b57cec5SDimitry Andric   if (!Sparse)
4140b57cec5SDimitry Andric     return true;
4150b57cec5SDimitry Andric   for (const auto &Func : PD) {
4160b57cec5SDimitry Andric     const InstrProfRecord &IPR = Func.second;
4170b57cec5SDimitry Andric     if (llvm::any_of(IPR.Counts, [](uint64_t Count) { return Count > 0; }))
4180b57cec5SDimitry Andric       return true;
4195f757f3fSDimitry Andric     if (llvm::any_of(IPR.BitmapBytes, [](uint8_t Byte) { return Byte > 0; }))
4205f757f3fSDimitry Andric       return true;
4210b57cec5SDimitry Andric   }
4220b57cec5SDimitry Andric   return false;
4230b57cec5SDimitry Andric }
4240b57cec5SDimitry Andric 
setSummary(IndexedInstrProf::Summary * TheSummary,ProfileSummary & PS)4250b57cec5SDimitry Andric static void setSummary(IndexedInstrProf::Summary *TheSummary,
4260b57cec5SDimitry Andric                        ProfileSummary &PS) {
4270b57cec5SDimitry Andric   using namespace IndexedInstrProf;
4280b57cec5SDimitry Andric 
429349cc55cSDimitry Andric   const std::vector<ProfileSummaryEntry> &Res = PS.getDetailedSummary();
4300b57cec5SDimitry Andric   TheSummary->NumSummaryFields = Summary::NumKinds;
4310b57cec5SDimitry Andric   TheSummary->NumCutoffEntries = Res.size();
4320b57cec5SDimitry Andric   TheSummary->set(Summary::MaxFunctionCount, PS.getMaxFunctionCount());
4330b57cec5SDimitry Andric   TheSummary->set(Summary::MaxBlockCount, PS.getMaxCount());
4340b57cec5SDimitry Andric   TheSummary->set(Summary::MaxInternalBlockCount, PS.getMaxInternalCount());
4350b57cec5SDimitry Andric   TheSummary->set(Summary::TotalBlockCount, PS.getTotalCount());
4360b57cec5SDimitry Andric   TheSummary->set(Summary::TotalNumBlocks, PS.getNumCounts());
4370b57cec5SDimitry Andric   TheSummary->set(Summary::TotalNumFunctions, PS.getNumFunctions());
4380b57cec5SDimitry Andric   for (unsigned I = 0; I < Res.size(); I++)
4390b57cec5SDimitry Andric     TheSummary->setEntry(I, Res[I]);
4400b57cec5SDimitry Andric }
4410b57cec5SDimitry Andric 
442*0fca6ea1SDimitry Andric // Serialize Schema.
writeMemProfSchema(ProfOStream & OS,const memprof::MemProfSchema & Schema)443*0fca6ea1SDimitry Andric static void writeMemProfSchema(ProfOStream &OS,
444*0fca6ea1SDimitry Andric                                const memprof::MemProfSchema &Schema) {
445*0fca6ea1SDimitry Andric   OS.write(static_cast<uint64_t>(Schema.size()));
446*0fca6ea1SDimitry Andric   for (const auto Id : Schema)
447*0fca6ea1SDimitry Andric     OS.write(static_cast<uint64_t>(Id));
448*0fca6ea1SDimitry Andric }
449*0fca6ea1SDimitry Andric 
450*0fca6ea1SDimitry Andric // Serialize MemProfRecordData.  Return RecordTableOffset.
writeMemProfRecords(ProfOStream & OS,llvm::MapVector<GlobalValue::GUID,memprof::IndexedMemProfRecord> & MemProfRecordData,memprof::MemProfSchema * Schema,memprof::IndexedVersion Version,llvm::DenseMap<memprof::CallStackId,memprof::LinearCallStackId> * MemProfCallStackIndexes=nullptr)451*0fca6ea1SDimitry Andric static uint64_t writeMemProfRecords(
452*0fca6ea1SDimitry Andric     ProfOStream &OS,
453*0fca6ea1SDimitry Andric     llvm::MapVector<GlobalValue::GUID, memprof::IndexedMemProfRecord>
454*0fca6ea1SDimitry Andric         &MemProfRecordData,
455*0fca6ea1SDimitry Andric     memprof::MemProfSchema *Schema, memprof::IndexedVersion Version,
456*0fca6ea1SDimitry Andric     llvm::DenseMap<memprof::CallStackId, memprof::LinearCallStackId>
457*0fca6ea1SDimitry Andric         *MemProfCallStackIndexes = nullptr) {
458*0fca6ea1SDimitry Andric   memprof::RecordWriterTrait RecordWriter(Schema, Version,
459*0fca6ea1SDimitry Andric                                           MemProfCallStackIndexes);
460*0fca6ea1SDimitry Andric   OnDiskChainedHashTableGenerator<memprof::RecordWriterTrait>
461*0fca6ea1SDimitry Andric       RecordTableGenerator;
462*0fca6ea1SDimitry Andric   for (auto &[GUID, Record] : MemProfRecordData) {
463*0fca6ea1SDimitry Andric     // Insert the key (func hash) and value (memprof record).
464*0fca6ea1SDimitry Andric     RecordTableGenerator.insert(GUID, Record, RecordWriter);
465*0fca6ea1SDimitry Andric   }
466*0fca6ea1SDimitry Andric   // Release the memory of this MapVector as it is no longer needed.
467*0fca6ea1SDimitry Andric   MemProfRecordData.clear();
468*0fca6ea1SDimitry Andric 
469*0fca6ea1SDimitry Andric   // The call to Emit invokes RecordWriterTrait::EmitData which destructs
470*0fca6ea1SDimitry Andric   // the memprof record copies owned by the RecordTableGenerator. This works
471*0fca6ea1SDimitry Andric   // because the RecordTableGenerator is not used after this point.
472*0fca6ea1SDimitry Andric   return RecordTableGenerator.Emit(OS.OS, RecordWriter);
473*0fca6ea1SDimitry Andric }
474*0fca6ea1SDimitry Andric 
475*0fca6ea1SDimitry Andric // Serialize MemProfFrameData.  Return FrameTableOffset.
writeMemProfFrames(ProfOStream & OS,llvm::MapVector<memprof::FrameId,memprof::Frame> & MemProfFrameData)476*0fca6ea1SDimitry Andric static uint64_t writeMemProfFrames(
477*0fca6ea1SDimitry Andric     ProfOStream &OS,
478*0fca6ea1SDimitry Andric     llvm::MapVector<memprof::FrameId, memprof::Frame> &MemProfFrameData) {
479*0fca6ea1SDimitry Andric   OnDiskChainedHashTableGenerator<memprof::FrameWriterTrait>
480*0fca6ea1SDimitry Andric       FrameTableGenerator;
481*0fca6ea1SDimitry Andric   for (auto &[FrameId, Frame] : MemProfFrameData) {
482*0fca6ea1SDimitry Andric     // Insert the key (frame id) and value (frame contents).
483*0fca6ea1SDimitry Andric     FrameTableGenerator.insert(FrameId, Frame);
484*0fca6ea1SDimitry Andric   }
485*0fca6ea1SDimitry Andric   // Release the memory of this MapVector as it is no longer needed.
486*0fca6ea1SDimitry Andric   MemProfFrameData.clear();
487*0fca6ea1SDimitry Andric 
488*0fca6ea1SDimitry Andric   return FrameTableGenerator.Emit(OS.OS);
489*0fca6ea1SDimitry Andric }
490*0fca6ea1SDimitry Andric 
491*0fca6ea1SDimitry Andric // Serialize MemProfFrameData.  Return the mapping from FrameIds to their
492*0fca6ea1SDimitry Andric // indexes within the frame array.
493*0fca6ea1SDimitry Andric static llvm::DenseMap<memprof::FrameId, memprof::LinearFrameId>
writeMemProfFrameArray(ProfOStream & OS,llvm::MapVector<memprof::FrameId,memprof::Frame> & MemProfFrameData,llvm::DenseMap<memprof::FrameId,memprof::FrameStat> & FrameHistogram)494*0fca6ea1SDimitry Andric writeMemProfFrameArray(
495*0fca6ea1SDimitry Andric     ProfOStream &OS,
496*0fca6ea1SDimitry Andric     llvm::MapVector<memprof::FrameId, memprof::Frame> &MemProfFrameData,
497*0fca6ea1SDimitry Andric     llvm::DenseMap<memprof::FrameId, memprof::FrameStat> &FrameHistogram) {
498*0fca6ea1SDimitry Andric   // Mappings from FrameIds to array indexes.
499*0fca6ea1SDimitry Andric   llvm::DenseMap<memprof::FrameId, memprof::LinearFrameId> MemProfFrameIndexes;
500*0fca6ea1SDimitry Andric 
501*0fca6ea1SDimitry Andric   // Compute the order in which we serialize Frames.  The order does not matter
502*0fca6ea1SDimitry Andric   // in terms of correctness, but we still compute it for deserialization
503*0fca6ea1SDimitry Andric   // performance.  Specifically, if we serialize frequently used Frames one
504*0fca6ea1SDimitry Andric   // after another, we have better cache utilization.  For two Frames that
505*0fca6ea1SDimitry Andric   // appear equally frequently, we break a tie by serializing the one that tends
506*0fca6ea1SDimitry Andric   // to appear earlier in call stacks.  We implement the tie-breaking mechanism
507*0fca6ea1SDimitry Andric   // by computing the sum of indexes within call stacks for each Frame.  If we
508*0fca6ea1SDimitry Andric   // still have a tie, then we just resort to compare two FrameIds, which is
509*0fca6ea1SDimitry Andric   // just for stability of output.
510*0fca6ea1SDimitry Andric   std::vector<std::pair<memprof::FrameId, const memprof::Frame *>> FrameIdOrder;
511*0fca6ea1SDimitry Andric   FrameIdOrder.reserve(MemProfFrameData.size());
512*0fca6ea1SDimitry Andric   for (const auto &[Id, Frame] : MemProfFrameData)
513*0fca6ea1SDimitry Andric     FrameIdOrder.emplace_back(Id, &Frame);
514*0fca6ea1SDimitry Andric   assert(MemProfFrameData.size() == FrameIdOrder.size());
515*0fca6ea1SDimitry Andric   llvm::sort(FrameIdOrder,
516*0fca6ea1SDimitry Andric              [&](const std::pair<memprof::FrameId, const memprof::Frame *> &L,
517*0fca6ea1SDimitry Andric                  const std::pair<memprof::FrameId, const memprof::Frame *> &R) {
518*0fca6ea1SDimitry Andric                const auto &SL = FrameHistogram[L.first];
519*0fca6ea1SDimitry Andric                const auto &SR = FrameHistogram[R.first];
520*0fca6ea1SDimitry Andric                // Popular FrameIds should come first.
521*0fca6ea1SDimitry Andric                if (SL.Count != SR.Count)
522*0fca6ea1SDimitry Andric                  return SL.Count > SR.Count;
523*0fca6ea1SDimitry Andric                // If they are equally popular, then the one that tends to appear
524*0fca6ea1SDimitry Andric                // earlier in call stacks should come first.
525*0fca6ea1SDimitry Andric                if (SL.PositionSum != SR.PositionSum)
526*0fca6ea1SDimitry Andric                  return SL.PositionSum < SR.PositionSum;
527*0fca6ea1SDimitry Andric                // Compare their FrameIds for sort stability.
528*0fca6ea1SDimitry Andric                return L.first < R.first;
529*0fca6ea1SDimitry Andric              });
530*0fca6ea1SDimitry Andric 
531*0fca6ea1SDimitry Andric   // Serialize all frames while creating mappings from linear IDs to FrameIds.
532*0fca6ea1SDimitry Andric   uint64_t Index = 0;
533*0fca6ea1SDimitry Andric   MemProfFrameIndexes.reserve(FrameIdOrder.size());
534*0fca6ea1SDimitry Andric   for (const auto &[Id, F] : FrameIdOrder) {
535*0fca6ea1SDimitry Andric     F->serialize(OS.OS);
536*0fca6ea1SDimitry Andric     MemProfFrameIndexes.insert({Id, Index});
537*0fca6ea1SDimitry Andric     ++Index;
538*0fca6ea1SDimitry Andric   }
539*0fca6ea1SDimitry Andric   assert(MemProfFrameData.size() == Index);
540*0fca6ea1SDimitry Andric   assert(MemProfFrameData.size() == MemProfFrameIndexes.size());
541*0fca6ea1SDimitry Andric 
542*0fca6ea1SDimitry Andric   // Release the memory of this MapVector as it is no longer needed.
543*0fca6ea1SDimitry Andric   MemProfFrameData.clear();
544*0fca6ea1SDimitry Andric 
545*0fca6ea1SDimitry Andric   return MemProfFrameIndexes;
546*0fca6ea1SDimitry Andric }
547*0fca6ea1SDimitry Andric 
writeMemProfCallStacks(ProfOStream & OS,llvm::MapVector<memprof::CallStackId,llvm::SmallVector<memprof::FrameId>> & MemProfCallStackData)548*0fca6ea1SDimitry Andric static uint64_t writeMemProfCallStacks(
549*0fca6ea1SDimitry Andric     ProfOStream &OS,
550*0fca6ea1SDimitry Andric     llvm::MapVector<memprof::CallStackId, llvm::SmallVector<memprof::FrameId>>
551*0fca6ea1SDimitry Andric         &MemProfCallStackData) {
552*0fca6ea1SDimitry Andric   OnDiskChainedHashTableGenerator<memprof::CallStackWriterTrait>
553*0fca6ea1SDimitry Andric       CallStackTableGenerator;
554*0fca6ea1SDimitry Andric   for (auto &[CSId, CallStack] : MemProfCallStackData)
555*0fca6ea1SDimitry Andric     CallStackTableGenerator.insert(CSId, CallStack);
556*0fca6ea1SDimitry Andric   // Release the memory of this vector as it is no longer needed.
557*0fca6ea1SDimitry Andric   MemProfCallStackData.clear();
558*0fca6ea1SDimitry Andric 
559*0fca6ea1SDimitry Andric   return CallStackTableGenerator.Emit(OS.OS);
560*0fca6ea1SDimitry Andric }
561*0fca6ea1SDimitry Andric 
562*0fca6ea1SDimitry Andric static llvm::DenseMap<memprof::CallStackId, memprof::LinearCallStackId>
writeMemProfCallStackArray(ProfOStream & OS,llvm::MapVector<memprof::CallStackId,llvm::SmallVector<memprof::FrameId>> & MemProfCallStackData,llvm::DenseMap<memprof::FrameId,memprof::LinearFrameId> & MemProfFrameIndexes,llvm::DenseMap<memprof::FrameId,memprof::FrameStat> & FrameHistogram)563*0fca6ea1SDimitry Andric writeMemProfCallStackArray(
564*0fca6ea1SDimitry Andric     ProfOStream &OS,
565*0fca6ea1SDimitry Andric     llvm::MapVector<memprof::CallStackId, llvm::SmallVector<memprof::FrameId>>
566*0fca6ea1SDimitry Andric         &MemProfCallStackData,
567*0fca6ea1SDimitry Andric     llvm::DenseMap<memprof::FrameId, memprof::LinearFrameId>
568*0fca6ea1SDimitry Andric         &MemProfFrameIndexes,
569*0fca6ea1SDimitry Andric     llvm::DenseMap<memprof::FrameId, memprof::FrameStat> &FrameHistogram) {
570*0fca6ea1SDimitry Andric   llvm::DenseMap<memprof::CallStackId, memprof::LinearCallStackId>
571*0fca6ea1SDimitry Andric       MemProfCallStackIndexes;
572*0fca6ea1SDimitry Andric 
573*0fca6ea1SDimitry Andric   memprof::CallStackRadixTreeBuilder Builder;
574*0fca6ea1SDimitry Andric   Builder.build(std::move(MemProfCallStackData), MemProfFrameIndexes,
575*0fca6ea1SDimitry Andric                 FrameHistogram);
576*0fca6ea1SDimitry Andric   for (auto I : Builder.getRadixArray())
577*0fca6ea1SDimitry Andric     OS.write32(I);
578*0fca6ea1SDimitry Andric   MemProfCallStackIndexes = Builder.takeCallStackPos();
579*0fca6ea1SDimitry Andric 
580*0fca6ea1SDimitry Andric   // Release the memory of this vector as it is no longer needed.
581*0fca6ea1SDimitry Andric   MemProfCallStackData.clear();
582*0fca6ea1SDimitry Andric 
583*0fca6ea1SDimitry Andric   return MemProfCallStackIndexes;
584*0fca6ea1SDimitry Andric }
585*0fca6ea1SDimitry Andric 
586*0fca6ea1SDimitry Andric // Write out MemProf Version0 as follows:
587*0fca6ea1SDimitry Andric // uint64_t RecordTableOffset = RecordTableGenerator.Emit
588*0fca6ea1SDimitry Andric // uint64_t FramePayloadOffset = Offset for the frame payload
589*0fca6ea1SDimitry Andric // uint64_t FrameTableOffset = FrameTableGenerator.Emit
590*0fca6ea1SDimitry Andric // uint64_t Num schema entries
591*0fca6ea1SDimitry Andric // uint64_t Schema entry 0
592*0fca6ea1SDimitry Andric // uint64_t Schema entry 1
593*0fca6ea1SDimitry Andric // ....
594*0fca6ea1SDimitry Andric // uint64_t Schema entry N - 1
595*0fca6ea1SDimitry Andric // OnDiskChainedHashTable MemProfRecordData
596*0fca6ea1SDimitry Andric // OnDiskChainedHashTable MemProfFrameData
writeMemProfV0(ProfOStream & OS,memprof::IndexedMemProfData & MemProfData)597*0fca6ea1SDimitry Andric static Error writeMemProfV0(ProfOStream &OS,
598*0fca6ea1SDimitry Andric                             memprof::IndexedMemProfData &MemProfData) {
599*0fca6ea1SDimitry Andric   uint64_t HeaderUpdatePos = OS.tell();
600*0fca6ea1SDimitry Andric   OS.write(0ULL); // Reserve space for the memprof record table offset.
601*0fca6ea1SDimitry Andric   OS.write(0ULL); // Reserve space for the memprof frame payload offset.
602*0fca6ea1SDimitry Andric   OS.write(0ULL); // Reserve space for the memprof frame table offset.
603*0fca6ea1SDimitry Andric 
604*0fca6ea1SDimitry Andric   auto Schema = memprof::getFullSchema();
605*0fca6ea1SDimitry Andric   writeMemProfSchema(OS, Schema);
606*0fca6ea1SDimitry Andric 
607*0fca6ea1SDimitry Andric   uint64_t RecordTableOffset =
608*0fca6ea1SDimitry Andric       writeMemProfRecords(OS, MemProfData.Records, &Schema, memprof::Version0);
609*0fca6ea1SDimitry Andric 
610*0fca6ea1SDimitry Andric   uint64_t FramePayloadOffset = OS.tell();
611*0fca6ea1SDimitry Andric   uint64_t FrameTableOffset = writeMemProfFrames(OS, MemProfData.Frames);
612*0fca6ea1SDimitry Andric 
613*0fca6ea1SDimitry Andric   uint64_t Header[] = {RecordTableOffset, FramePayloadOffset, FrameTableOffset};
614*0fca6ea1SDimitry Andric   OS.patch({{HeaderUpdatePos, Header}});
615*0fca6ea1SDimitry Andric 
616*0fca6ea1SDimitry Andric   return Error::success();
617*0fca6ea1SDimitry Andric }
618*0fca6ea1SDimitry Andric 
619*0fca6ea1SDimitry Andric // Write out MemProf Version1 as follows:
620*0fca6ea1SDimitry Andric // uint64_t Version (NEW in V1)
621*0fca6ea1SDimitry Andric // uint64_t RecordTableOffset = RecordTableGenerator.Emit
622*0fca6ea1SDimitry Andric // uint64_t FramePayloadOffset = Offset for the frame payload
623*0fca6ea1SDimitry Andric // uint64_t FrameTableOffset = FrameTableGenerator.Emit
624*0fca6ea1SDimitry Andric // uint64_t Num schema entries
625*0fca6ea1SDimitry Andric // uint64_t Schema entry 0
626*0fca6ea1SDimitry Andric // uint64_t Schema entry 1
627*0fca6ea1SDimitry Andric // ....
628*0fca6ea1SDimitry Andric // uint64_t Schema entry N - 1
629*0fca6ea1SDimitry Andric // OnDiskChainedHashTable MemProfRecordData
630*0fca6ea1SDimitry Andric // OnDiskChainedHashTable MemProfFrameData
writeMemProfV1(ProfOStream & OS,memprof::IndexedMemProfData & MemProfData)631*0fca6ea1SDimitry Andric static Error writeMemProfV1(ProfOStream &OS,
632*0fca6ea1SDimitry Andric                             memprof::IndexedMemProfData &MemProfData) {
633*0fca6ea1SDimitry Andric   OS.write(memprof::Version1);
634*0fca6ea1SDimitry Andric   uint64_t HeaderUpdatePos = OS.tell();
635*0fca6ea1SDimitry Andric   OS.write(0ULL); // Reserve space for the memprof record table offset.
636*0fca6ea1SDimitry Andric   OS.write(0ULL); // Reserve space for the memprof frame payload offset.
637*0fca6ea1SDimitry Andric   OS.write(0ULL); // Reserve space for the memprof frame table offset.
638*0fca6ea1SDimitry Andric 
639*0fca6ea1SDimitry Andric   auto Schema = memprof::getFullSchema();
640*0fca6ea1SDimitry Andric   writeMemProfSchema(OS, Schema);
641*0fca6ea1SDimitry Andric 
642*0fca6ea1SDimitry Andric   uint64_t RecordTableOffset =
643*0fca6ea1SDimitry Andric       writeMemProfRecords(OS, MemProfData.Records, &Schema, memprof::Version1);
644*0fca6ea1SDimitry Andric 
645*0fca6ea1SDimitry Andric   uint64_t FramePayloadOffset = OS.tell();
646*0fca6ea1SDimitry Andric   uint64_t FrameTableOffset = writeMemProfFrames(OS, MemProfData.Frames);
647*0fca6ea1SDimitry Andric 
648*0fca6ea1SDimitry Andric   uint64_t Header[] = {RecordTableOffset, FramePayloadOffset, FrameTableOffset};
649*0fca6ea1SDimitry Andric   OS.patch({{HeaderUpdatePos, Header}});
650*0fca6ea1SDimitry Andric 
651*0fca6ea1SDimitry Andric   return Error::success();
652*0fca6ea1SDimitry Andric }
653*0fca6ea1SDimitry Andric 
654*0fca6ea1SDimitry Andric // Write out MemProf Version2 as follows:
655*0fca6ea1SDimitry Andric // uint64_t Version
656*0fca6ea1SDimitry Andric // uint64_t RecordTableOffset = RecordTableGenerator.Emit
657*0fca6ea1SDimitry Andric // uint64_t FramePayloadOffset = Offset for the frame payload
658*0fca6ea1SDimitry Andric // uint64_t FrameTableOffset = FrameTableGenerator.Emit
659*0fca6ea1SDimitry Andric // uint64_t CallStackPayloadOffset = Offset for the call stack payload (NEW V2)
660*0fca6ea1SDimitry Andric // uint64_t CallStackTableOffset = CallStackTableGenerator.Emit (NEW in V2)
661*0fca6ea1SDimitry Andric // uint64_t Num schema entries
662*0fca6ea1SDimitry Andric // uint64_t Schema entry 0
663*0fca6ea1SDimitry Andric // uint64_t Schema entry 1
664*0fca6ea1SDimitry Andric // ....
665*0fca6ea1SDimitry Andric // uint64_t Schema entry N - 1
666*0fca6ea1SDimitry Andric // OnDiskChainedHashTable MemProfRecordData
667*0fca6ea1SDimitry Andric // OnDiskChainedHashTable MemProfFrameData
668*0fca6ea1SDimitry Andric // OnDiskChainedHashTable MemProfCallStackData (NEW in V2)
writeMemProfV2(ProfOStream & OS,memprof::IndexedMemProfData & MemProfData,bool MemProfFullSchema)669*0fca6ea1SDimitry Andric static Error writeMemProfV2(ProfOStream &OS,
670*0fca6ea1SDimitry Andric                             memprof::IndexedMemProfData &MemProfData,
671*0fca6ea1SDimitry Andric                             bool MemProfFullSchema) {
672*0fca6ea1SDimitry Andric   OS.write(memprof::Version2);
673*0fca6ea1SDimitry Andric   uint64_t HeaderUpdatePos = OS.tell();
674*0fca6ea1SDimitry Andric   OS.write(0ULL); // Reserve space for the memprof record table offset.
675*0fca6ea1SDimitry Andric   OS.write(0ULL); // Reserve space for the memprof frame payload offset.
676*0fca6ea1SDimitry Andric   OS.write(0ULL); // Reserve space for the memprof frame table offset.
677*0fca6ea1SDimitry Andric   OS.write(0ULL); // Reserve space for the memprof call stack payload offset.
678*0fca6ea1SDimitry Andric   OS.write(0ULL); // Reserve space for the memprof call stack table offset.
679*0fca6ea1SDimitry Andric 
680*0fca6ea1SDimitry Andric   auto Schema = memprof::getHotColdSchema();
681*0fca6ea1SDimitry Andric   if (MemProfFullSchema)
682*0fca6ea1SDimitry Andric     Schema = memprof::getFullSchema();
683*0fca6ea1SDimitry Andric   writeMemProfSchema(OS, Schema);
684*0fca6ea1SDimitry Andric 
685*0fca6ea1SDimitry Andric   uint64_t RecordTableOffset =
686*0fca6ea1SDimitry Andric       writeMemProfRecords(OS, MemProfData.Records, &Schema, memprof::Version2);
687*0fca6ea1SDimitry Andric 
688*0fca6ea1SDimitry Andric   uint64_t FramePayloadOffset = OS.tell();
689*0fca6ea1SDimitry Andric   uint64_t FrameTableOffset = writeMemProfFrames(OS, MemProfData.Frames);
690*0fca6ea1SDimitry Andric 
691*0fca6ea1SDimitry Andric   uint64_t CallStackPayloadOffset = OS.tell();
692*0fca6ea1SDimitry Andric   uint64_t CallStackTableOffset =
693*0fca6ea1SDimitry Andric       writeMemProfCallStacks(OS, MemProfData.CallStacks);
694*0fca6ea1SDimitry Andric 
695*0fca6ea1SDimitry Andric   uint64_t Header[] = {
696*0fca6ea1SDimitry Andric       RecordTableOffset,      FramePayloadOffset,   FrameTableOffset,
697*0fca6ea1SDimitry Andric       CallStackPayloadOffset, CallStackTableOffset,
698*0fca6ea1SDimitry Andric   };
699*0fca6ea1SDimitry Andric   OS.patch({{HeaderUpdatePos, Header}});
700*0fca6ea1SDimitry Andric 
701*0fca6ea1SDimitry Andric   return Error::success();
702*0fca6ea1SDimitry Andric }
703*0fca6ea1SDimitry Andric 
704*0fca6ea1SDimitry Andric // Write out MemProf Version3 as follows:
705*0fca6ea1SDimitry Andric // uint64_t Version
706*0fca6ea1SDimitry Andric // uint64_t CallStackPayloadOffset = Offset for the call stack payload
707*0fca6ea1SDimitry Andric // uint64_t RecordPayloadOffset = Offset for the record payload
708*0fca6ea1SDimitry Andric // uint64_t RecordTableOffset = RecordTableGenerator.Emit
709*0fca6ea1SDimitry Andric // uint64_t Num schema entries
710*0fca6ea1SDimitry Andric // uint64_t Schema entry 0
711*0fca6ea1SDimitry Andric // uint64_t Schema entry 1
712*0fca6ea1SDimitry Andric // ....
713*0fca6ea1SDimitry Andric // uint64_t Schema entry N - 1
714*0fca6ea1SDimitry Andric // Frames serialized one after another
715*0fca6ea1SDimitry Andric // Call stacks encoded as a radix tree
716*0fca6ea1SDimitry Andric // OnDiskChainedHashTable MemProfRecordData
writeMemProfV3(ProfOStream & OS,memprof::IndexedMemProfData & MemProfData,bool MemProfFullSchema)717*0fca6ea1SDimitry Andric static Error writeMemProfV3(ProfOStream &OS,
718*0fca6ea1SDimitry Andric                             memprof::IndexedMemProfData &MemProfData,
719*0fca6ea1SDimitry Andric                             bool MemProfFullSchema) {
720*0fca6ea1SDimitry Andric   OS.write(memprof::Version3);
721*0fca6ea1SDimitry Andric   uint64_t HeaderUpdatePos = OS.tell();
722*0fca6ea1SDimitry Andric   OS.write(0ULL); // Reserve space for the memprof call stack payload offset.
723*0fca6ea1SDimitry Andric   OS.write(0ULL); // Reserve space for the memprof record payload offset.
724*0fca6ea1SDimitry Andric   OS.write(0ULL); // Reserve space for the memprof record table offset.
725*0fca6ea1SDimitry Andric 
726*0fca6ea1SDimitry Andric   auto Schema = memprof::getHotColdSchema();
727*0fca6ea1SDimitry Andric   if (MemProfFullSchema)
728*0fca6ea1SDimitry Andric     Schema = memprof::getFullSchema();
729*0fca6ea1SDimitry Andric   writeMemProfSchema(OS, Schema);
730*0fca6ea1SDimitry Andric 
731*0fca6ea1SDimitry Andric   llvm::DenseMap<memprof::FrameId, memprof::FrameStat> FrameHistogram =
732*0fca6ea1SDimitry Andric       memprof::computeFrameHistogram(MemProfData.CallStacks);
733*0fca6ea1SDimitry Andric   assert(MemProfData.Frames.size() == FrameHistogram.size());
734*0fca6ea1SDimitry Andric 
735*0fca6ea1SDimitry Andric   llvm::DenseMap<memprof::FrameId, memprof::LinearFrameId> MemProfFrameIndexes =
736*0fca6ea1SDimitry Andric       writeMemProfFrameArray(OS, MemProfData.Frames, FrameHistogram);
737*0fca6ea1SDimitry Andric 
738*0fca6ea1SDimitry Andric   uint64_t CallStackPayloadOffset = OS.tell();
739*0fca6ea1SDimitry Andric   llvm::DenseMap<memprof::CallStackId, memprof::LinearCallStackId>
740*0fca6ea1SDimitry Andric       MemProfCallStackIndexes = writeMemProfCallStackArray(
741*0fca6ea1SDimitry Andric           OS, MemProfData.CallStacks, MemProfFrameIndexes, FrameHistogram);
742*0fca6ea1SDimitry Andric 
743*0fca6ea1SDimitry Andric   uint64_t RecordPayloadOffset = OS.tell();
744*0fca6ea1SDimitry Andric   uint64_t RecordTableOffset =
745*0fca6ea1SDimitry Andric       writeMemProfRecords(OS, MemProfData.Records, &Schema, memprof::Version3,
746*0fca6ea1SDimitry Andric                           &MemProfCallStackIndexes);
747*0fca6ea1SDimitry Andric 
748*0fca6ea1SDimitry Andric   uint64_t Header[] = {
749*0fca6ea1SDimitry Andric       CallStackPayloadOffset,
750*0fca6ea1SDimitry Andric       RecordPayloadOffset,
751*0fca6ea1SDimitry Andric       RecordTableOffset,
752*0fca6ea1SDimitry Andric   };
753*0fca6ea1SDimitry Andric   OS.patch({{HeaderUpdatePos, Header}});
754*0fca6ea1SDimitry Andric 
755*0fca6ea1SDimitry Andric   return Error::success();
756*0fca6ea1SDimitry Andric }
757*0fca6ea1SDimitry Andric 
758*0fca6ea1SDimitry Andric // Write out the MemProf data in a requested version.
writeMemProf(ProfOStream & OS,memprof::IndexedMemProfData & MemProfData,memprof::IndexedVersion MemProfVersionRequested,bool MemProfFullSchema)759*0fca6ea1SDimitry Andric static Error writeMemProf(ProfOStream &OS,
760*0fca6ea1SDimitry Andric                           memprof::IndexedMemProfData &MemProfData,
761*0fca6ea1SDimitry Andric                           memprof::IndexedVersion MemProfVersionRequested,
762*0fca6ea1SDimitry Andric                           bool MemProfFullSchema) {
763*0fca6ea1SDimitry Andric   switch (MemProfVersionRequested) {
764*0fca6ea1SDimitry Andric   case memprof::Version0:
765*0fca6ea1SDimitry Andric     return writeMemProfV0(OS, MemProfData);
766*0fca6ea1SDimitry Andric   case memprof::Version1:
767*0fca6ea1SDimitry Andric     return writeMemProfV1(OS, MemProfData);
768*0fca6ea1SDimitry Andric   case memprof::Version2:
769*0fca6ea1SDimitry Andric     return writeMemProfV2(OS, MemProfData, MemProfFullSchema);
770*0fca6ea1SDimitry Andric   case memprof::Version3:
771*0fca6ea1SDimitry Andric     return writeMemProfV3(OS, MemProfData, MemProfFullSchema);
772*0fca6ea1SDimitry Andric   }
773*0fca6ea1SDimitry Andric 
774*0fca6ea1SDimitry Andric   return make_error<InstrProfError>(
775*0fca6ea1SDimitry Andric       instrprof_error::unsupported_version,
776*0fca6ea1SDimitry Andric       formatv("MemProf version {} not supported; "
777*0fca6ea1SDimitry Andric               "requires version between {} and {}, inclusive",
778*0fca6ea1SDimitry Andric               MemProfVersionRequested, memprof::MinimumSupportedVersion,
779*0fca6ea1SDimitry Andric               memprof::MaximumSupportedVersion));
780*0fca6ea1SDimitry Andric }
781*0fca6ea1SDimitry Andric 
writeHeader(const IndexedInstrProf::Header & Header,const bool WritePrevVersion,ProfOStream & OS)782*0fca6ea1SDimitry Andric uint64_t InstrProfWriter::writeHeader(const IndexedInstrProf::Header &Header,
783*0fca6ea1SDimitry Andric                                       const bool WritePrevVersion,
784*0fca6ea1SDimitry Andric                                       ProfOStream &OS) {
785*0fca6ea1SDimitry Andric   // Only write out the first four fields.
786*0fca6ea1SDimitry Andric   for (int I = 0; I < 4; I++)
787*0fca6ea1SDimitry Andric     OS.write(reinterpret_cast<const uint64_t *>(&Header)[I]);
788*0fca6ea1SDimitry Andric 
789*0fca6ea1SDimitry Andric   // Remember the offset of the remaining fields to allow back patching later.
790*0fca6ea1SDimitry Andric   auto BackPatchStartOffset = OS.tell();
791*0fca6ea1SDimitry Andric 
792*0fca6ea1SDimitry Andric   // Reserve the space for back patching later.
793*0fca6ea1SDimitry Andric   OS.write(0); // HashOffset
794*0fca6ea1SDimitry Andric   OS.write(0); // MemProfOffset
795*0fca6ea1SDimitry Andric   OS.write(0); // BinaryIdOffset
796*0fca6ea1SDimitry Andric   OS.write(0); // TemporalProfTracesOffset
797*0fca6ea1SDimitry Andric   if (!WritePrevVersion)
798*0fca6ea1SDimitry Andric     OS.write(0); // VTableNamesOffset
799*0fca6ea1SDimitry Andric 
800*0fca6ea1SDimitry Andric   return BackPatchStartOffset;
801*0fca6ea1SDimitry Andric }
802*0fca6ea1SDimitry Andric 
writeVTableNames(ProfOStream & OS)803*0fca6ea1SDimitry Andric Error InstrProfWriter::writeVTableNames(ProfOStream &OS) {
804*0fca6ea1SDimitry Andric   std::vector<std::string> VTableNameStrs;
805*0fca6ea1SDimitry Andric   for (StringRef VTableName : VTableNames.keys())
806*0fca6ea1SDimitry Andric     VTableNameStrs.push_back(VTableName.str());
807*0fca6ea1SDimitry Andric 
808*0fca6ea1SDimitry Andric   std::string CompressedVTableNames;
809*0fca6ea1SDimitry Andric   if (!VTableNameStrs.empty())
810*0fca6ea1SDimitry Andric     if (Error E = collectGlobalObjectNameStrings(
811*0fca6ea1SDimitry Andric             VTableNameStrs, compression::zlib::isAvailable(),
812*0fca6ea1SDimitry Andric             CompressedVTableNames))
813*0fca6ea1SDimitry Andric       return E;
814*0fca6ea1SDimitry Andric 
815*0fca6ea1SDimitry Andric   const uint64_t CompressedStringLen = CompressedVTableNames.length();
816*0fca6ea1SDimitry Andric 
817*0fca6ea1SDimitry Andric   // Record the length of compressed string.
818*0fca6ea1SDimitry Andric   OS.write(CompressedStringLen);
819*0fca6ea1SDimitry Andric 
820*0fca6ea1SDimitry Andric   // Write the chars in compressed strings.
821*0fca6ea1SDimitry Andric   for (auto &c : CompressedVTableNames)
822*0fca6ea1SDimitry Andric     OS.writeByte(static_cast<uint8_t>(c));
823*0fca6ea1SDimitry Andric 
824*0fca6ea1SDimitry Andric   // Pad up to a multiple of 8.
825*0fca6ea1SDimitry Andric   // InstrProfReader could read bytes according to 'CompressedStringLen'.
826*0fca6ea1SDimitry Andric   const uint64_t PaddedLength = alignTo(CompressedStringLen, 8);
827*0fca6ea1SDimitry Andric 
828*0fca6ea1SDimitry Andric   for (uint64_t K = CompressedStringLen; K < PaddedLength; K++)
829*0fca6ea1SDimitry Andric     OS.writeByte(0);
830*0fca6ea1SDimitry Andric 
831*0fca6ea1SDimitry Andric   return Error::success();
832*0fca6ea1SDimitry Andric }
833*0fca6ea1SDimitry Andric 
writeImpl(ProfOStream & OS)834fe6060f1SDimitry Andric Error InstrProfWriter::writeImpl(ProfOStream &OS) {
8350b57cec5SDimitry Andric   using namespace IndexedInstrProf;
836bdd1243dSDimitry Andric   using namespace support;
8370b57cec5SDimitry Andric 
8380b57cec5SDimitry Andric   OnDiskChainedHashTableGenerator<InstrProfRecordWriterTrait> Generator;
8390b57cec5SDimitry Andric 
8400b57cec5SDimitry Andric   InstrProfSummaryBuilder ISB(ProfileSummaryBuilder::DefaultCutoffs);
8410b57cec5SDimitry Andric   InfoObj->SummaryBuilder = &ISB;
8420b57cec5SDimitry Andric   InstrProfSummaryBuilder CSISB(ProfileSummaryBuilder::DefaultCutoffs);
8430b57cec5SDimitry Andric   InfoObj->CSSummaryBuilder = &CSISB;
8440b57cec5SDimitry Andric 
8450b57cec5SDimitry Andric   // Populate the hash table generator.
846*0fca6ea1SDimitry Andric   SmallVector<std::pair<StringRef, const ProfilingData *>> OrderedData;
8470b57cec5SDimitry Andric   for (const auto &I : FunctionData)
8480b57cec5SDimitry Andric     if (shouldEncodeData(I.getValue()))
84906c3fb27SDimitry Andric       OrderedData.emplace_back((I.getKey()), &I.getValue());
85006c3fb27SDimitry Andric   llvm::sort(OrderedData, less_first());
85106c3fb27SDimitry Andric   for (const auto &I : OrderedData)
85206c3fb27SDimitry Andric     Generator.insert(I.first, I.second);
85381ad6265SDimitry Andric 
8540b57cec5SDimitry Andric   // Write the header.
8550b57cec5SDimitry Andric   IndexedInstrProf::Header Header;
856*0fca6ea1SDimitry Andric   Header.Version = WritePrevVersion
857*0fca6ea1SDimitry Andric                        ? IndexedInstrProf::ProfVersion::Version11
858*0fca6ea1SDimitry Andric                        : IndexedInstrProf::ProfVersion::CurrentVersion;
859*0fca6ea1SDimitry Andric   // The WritePrevVersion handling will either need to be removed or updated
860*0fca6ea1SDimitry Andric   // if the version is advanced beyond 12.
861*0fca6ea1SDimitry Andric   static_assert(IndexedInstrProf::ProfVersion::CurrentVersion ==
862*0fca6ea1SDimitry Andric                 IndexedInstrProf::ProfVersion::Version12);
86381ad6265SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::IRInstrumentation))
8640b57cec5SDimitry Andric     Header.Version |= VARIANT_MASK_IR_PROF;
86581ad6265SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::ContextSensitive))
8660b57cec5SDimitry Andric     Header.Version |= VARIANT_MASK_CSIR_PROF;
86781ad6265SDimitry Andric   if (static_cast<bool>(ProfileKind &
86881ad6265SDimitry Andric                         InstrProfKind::FunctionEntryInstrumentation))
869e8d8bef9SDimitry Andric     Header.Version |= VARIANT_MASK_INSTR_ENTRY;
8701fd87a68SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::SingleByteCoverage))
8711fd87a68SDimitry Andric     Header.Version |= VARIANT_MASK_BYTE_COVERAGE;
8721fd87a68SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::FunctionEntryOnly))
8731fd87a68SDimitry Andric     Header.Version |= VARIANT_MASK_FUNCTION_ENTRY_ONLY;
87481ad6265SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::MemProf))
87581ad6265SDimitry Andric     Header.Version |= VARIANT_MASK_MEMPROF;
87606c3fb27SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::TemporalProfile))
87706c3fb27SDimitry Andric     Header.Version |= VARIANT_MASK_TEMPORAL_PROF;
878e8d8bef9SDimitry Andric 
879*0fca6ea1SDimitry Andric   const uint64_t BackPatchStartOffset =
880*0fca6ea1SDimitry Andric       writeHeader(Header, WritePrevVersion, OS);
88106c3fb27SDimitry Andric 
8820b57cec5SDimitry Andric   // Reserve space to write profile summary data.
8830b57cec5SDimitry Andric   uint32_t NumEntries = ProfileSummaryBuilder::DefaultCutoffs.size();
8840b57cec5SDimitry Andric   uint32_t SummarySize = Summary::getSize(Summary::NumKinds, NumEntries);
8850b57cec5SDimitry Andric   // Remember the summary offset.
8860b57cec5SDimitry Andric   uint64_t SummaryOffset = OS.tell();
8870b57cec5SDimitry Andric   for (unsigned I = 0; I < SummarySize / sizeof(uint64_t); I++)
8880b57cec5SDimitry Andric     OS.write(0);
8890b57cec5SDimitry Andric   uint64_t CSSummaryOffset = 0;
8900b57cec5SDimitry Andric   uint64_t CSSummarySize = 0;
89181ad6265SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::ContextSensitive)) {
8920b57cec5SDimitry Andric     CSSummaryOffset = OS.tell();
8930b57cec5SDimitry Andric     CSSummarySize = SummarySize / sizeof(uint64_t);
8940b57cec5SDimitry Andric     for (unsigned I = 0; I < CSSummarySize; I++)
8950b57cec5SDimitry Andric       OS.write(0);
8960b57cec5SDimitry Andric   }
8970b57cec5SDimitry Andric 
8980b57cec5SDimitry Andric   // Write the hash table.
8990b57cec5SDimitry Andric   uint64_t HashTableStart = Generator.Emit(OS.OS, *InfoObj);
9000b57cec5SDimitry Andric 
901*0fca6ea1SDimitry Andric   // Write the MemProf profile data if we have it.
90281ad6265SDimitry Andric   uint64_t MemProfSectionStart = 0;
90381ad6265SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::MemProf)) {
90481ad6265SDimitry Andric     MemProfSectionStart = OS.tell();
905*0fca6ea1SDimitry Andric     if (auto E = writeMemProf(OS, MemProfData, MemProfVersionRequested,
906*0fca6ea1SDimitry Andric                               MemProfFullSchema))
907*0fca6ea1SDimitry Andric       return E;
90881ad6265SDimitry Andric   }
90981ad6265SDimitry Andric 
910bdd1243dSDimitry Andric   // BinaryIdSection has two parts:
911bdd1243dSDimitry Andric   // 1. uint64_t BinaryIdsSectionSize
912bdd1243dSDimitry Andric   // 2. list of binary ids that consist of:
913bdd1243dSDimitry Andric   //    a. uint64_t BinaryIdLength
914bdd1243dSDimitry Andric   //    b. uint8_t  BinaryIdData
915bdd1243dSDimitry Andric   //    c. uint8_t  Padding (if necessary)
916bdd1243dSDimitry Andric   uint64_t BinaryIdSectionStart = OS.tell();
917bdd1243dSDimitry Andric   // Calculate size of binary section.
918bdd1243dSDimitry Andric   uint64_t BinaryIdsSectionSize = 0;
919bdd1243dSDimitry Andric 
920bdd1243dSDimitry Andric   // Remove duplicate binary ids.
921bdd1243dSDimitry Andric   llvm::sort(BinaryIds);
922*0fca6ea1SDimitry Andric   BinaryIds.erase(llvm::unique(BinaryIds), BinaryIds.end());
923bdd1243dSDimitry Andric 
924*0fca6ea1SDimitry Andric   for (const auto &BI : BinaryIds) {
925bdd1243dSDimitry Andric     // Increment by binary id length data type size.
926bdd1243dSDimitry Andric     BinaryIdsSectionSize += sizeof(uint64_t);
927bdd1243dSDimitry Andric     // Increment by binary id data length, aligned to 8 bytes.
928bdd1243dSDimitry Andric     BinaryIdsSectionSize += alignToPowerOf2(BI.size(), sizeof(uint64_t));
929bdd1243dSDimitry Andric   }
930bdd1243dSDimitry Andric   // Write binary ids section size.
931bdd1243dSDimitry Andric   OS.write(BinaryIdsSectionSize);
932bdd1243dSDimitry Andric 
933*0fca6ea1SDimitry Andric   for (const auto &BI : BinaryIds) {
934bdd1243dSDimitry Andric     uint64_t BILen = BI.size();
935bdd1243dSDimitry Andric     // Write binary id length.
936bdd1243dSDimitry Andric     OS.write(BILen);
937bdd1243dSDimitry Andric     // Write binary id data.
938bdd1243dSDimitry Andric     for (unsigned K = 0; K < BILen; K++)
939bdd1243dSDimitry Andric       OS.writeByte(BI[K]);
940bdd1243dSDimitry Andric     // Write padding if necessary.
941bdd1243dSDimitry Andric     uint64_t PaddingSize = alignToPowerOf2(BILen, sizeof(uint64_t)) - BILen;
942bdd1243dSDimitry Andric     for (unsigned K = 0; K < PaddingSize; K++)
943bdd1243dSDimitry Andric       OS.writeByte(0);
944bdd1243dSDimitry Andric   }
945bdd1243dSDimitry Andric 
946*0fca6ea1SDimitry Andric   uint64_t VTableNamesSectionStart = OS.tell();
947*0fca6ea1SDimitry Andric 
948*0fca6ea1SDimitry Andric   if (!WritePrevVersion)
949*0fca6ea1SDimitry Andric     if (Error E = writeVTableNames(OS))
950*0fca6ea1SDimitry Andric       return E;
951*0fca6ea1SDimitry Andric 
95206c3fb27SDimitry Andric   uint64_t TemporalProfTracesSectionStart = 0;
95306c3fb27SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::TemporalProfile)) {
95406c3fb27SDimitry Andric     TemporalProfTracesSectionStart = OS.tell();
95506c3fb27SDimitry Andric     OS.write(TemporalProfTraces.size());
95606c3fb27SDimitry Andric     OS.write(TemporalProfTraceStreamSize);
95706c3fb27SDimitry Andric     for (auto &Trace : TemporalProfTraces) {
95806c3fb27SDimitry Andric       OS.write(Trace.Weight);
95906c3fb27SDimitry Andric       OS.write(Trace.FunctionNameRefs.size());
96006c3fb27SDimitry Andric       for (auto &NameRef : Trace.FunctionNameRefs)
96106c3fb27SDimitry Andric         OS.write(NameRef);
96206c3fb27SDimitry Andric     }
96306c3fb27SDimitry Andric   }
96406c3fb27SDimitry Andric 
9650b57cec5SDimitry Andric   // Allocate space for data to be serialized out.
9660b57cec5SDimitry Andric   std::unique_ptr<IndexedInstrProf::Summary> TheSummary =
9670b57cec5SDimitry Andric       IndexedInstrProf::allocSummary(SummarySize);
9680b57cec5SDimitry Andric   // Compute the Summary and copy the data to the data
9690b57cec5SDimitry Andric   // structure to be serialized out (to disk or buffer).
9700b57cec5SDimitry Andric   std::unique_ptr<ProfileSummary> PS = ISB.getSummary();
9710b57cec5SDimitry Andric   setSummary(TheSummary.get(), *PS);
9720b57cec5SDimitry Andric   InfoObj->SummaryBuilder = nullptr;
9730b57cec5SDimitry Andric 
9740b57cec5SDimitry Andric   // For Context Sensitive summary.
9750b57cec5SDimitry Andric   std::unique_ptr<IndexedInstrProf::Summary> TheCSSummary = nullptr;
97681ad6265SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::ContextSensitive)) {
9770b57cec5SDimitry Andric     TheCSSummary = IndexedInstrProf::allocSummary(SummarySize);
9780b57cec5SDimitry Andric     std::unique_ptr<ProfileSummary> CSPS = CSISB.getSummary();
9790b57cec5SDimitry Andric     setSummary(TheCSSummary.get(), *CSPS);
9800b57cec5SDimitry Andric   }
9810b57cec5SDimitry Andric   InfoObj->CSSummaryBuilder = nullptr;
9820b57cec5SDimitry Andric 
983*0fca6ea1SDimitry Andric   SmallVector<uint64_t, 8> HeaderOffsets = {HashTableStart, MemProfSectionStart,
984*0fca6ea1SDimitry Andric                                             BinaryIdSectionStart,
985*0fca6ea1SDimitry Andric                                             TemporalProfTracesSectionStart};
986*0fca6ea1SDimitry Andric   if (!WritePrevVersion)
987*0fca6ea1SDimitry Andric     HeaderOffsets.push_back(VTableNamesSectionStart);
9880b57cec5SDimitry Andric 
989*0fca6ea1SDimitry Andric   PatchItem PatchItems[] = {
990*0fca6ea1SDimitry Andric       // Patch the Header fields
991*0fca6ea1SDimitry Andric       {BackPatchStartOffset, HeaderOffsets},
992*0fca6ea1SDimitry Andric       // Patch the summary data.
993*0fca6ea1SDimitry Andric       {SummaryOffset,
994*0fca6ea1SDimitry Andric        ArrayRef<uint64_t>(reinterpret_cast<uint64_t *>(TheSummary.get()),
995*0fca6ea1SDimitry Andric                           SummarySize / sizeof(uint64_t))},
996*0fca6ea1SDimitry Andric       {CSSummaryOffset,
997*0fca6ea1SDimitry Andric        ArrayRef<uint64_t>(reinterpret_cast<uint64_t *>(TheCSSummary.get()),
998*0fca6ea1SDimitry Andric                           CSSummarySize)}};
999*0fca6ea1SDimitry Andric 
1000*0fca6ea1SDimitry Andric   OS.patch(PatchItems);
1001fe6060f1SDimitry Andric 
1002fe6060f1SDimitry Andric   for (const auto &I : FunctionData)
1003fe6060f1SDimitry Andric     for (const auto &F : I.getValue())
1004fe6060f1SDimitry Andric       if (Error E = validateRecord(F.second))
1005fe6060f1SDimitry Andric         return E;
1006fe6060f1SDimitry Andric 
1007fe6060f1SDimitry Andric   return Error::success();
10080b57cec5SDimitry Andric }
10090b57cec5SDimitry Andric 
write(raw_fd_ostream & OS)1010fe6060f1SDimitry Andric Error InstrProfWriter::write(raw_fd_ostream &OS) {
10110b57cec5SDimitry Andric   // Write the hash table.
10120b57cec5SDimitry Andric   ProfOStream POS(OS);
1013fe6060f1SDimitry Andric   return writeImpl(POS);
10140b57cec5SDimitry Andric }
10150b57cec5SDimitry Andric 
write(raw_string_ostream & OS)101606c3fb27SDimitry Andric Error InstrProfWriter::write(raw_string_ostream &OS) {
101706c3fb27SDimitry Andric   ProfOStream POS(OS);
101806c3fb27SDimitry Andric   return writeImpl(POS);
101906c3fb27SDimitry Andric }
102006c3fb27SDimitry Andric 
writeBuffer()10210b57cec5SDimitry Andric std::unique_ptr<MemoryBuffer> InstrProfWriter::writeBuffer() {
10220b57cec5SDimitry Andric   std::string Data;
10230b57cec5SDimitry Andric   raw_string_ostream OS(Data);
10240b57cec5SDimitry Andric   // Write the hash table.
102506c3fb27SDimitry Andric   if (Error E = write(OS))
1026fe6060f1SDimitry Andric     return nullptr;
10270b57cec5SDimitry Andric   // Return this in an aligned memory buffer.
10280b57cec5SDimitry Andric   return MemoryBuffer::getMemBufferCopy(Data);
10290b57cec5SDimitry Andric }
10300b57cec5SDimitry Andric 
10310b57cec5SDimitry Andric static const char *ValueProfKindStr[] = {
10320b57cec5SDimitry Andric #define VALUE_PROF_KIND(Enumerator, Value, Descr) #Enumerator,
10330b57cec5SDimitry Andric #include "llvm/ProfileData/InstrProfData.inc"
10340b57cec5SDimitry Andric };
10350b57cec5SDimitry Andric 
validateRecord(const InstrProfRecord & Func)1036fe6060f1SDimitry Andric Error InstrProfWriter::validateRecord(const InstrProfRecord &Func) {
1037fe6060f1SDimitry Andric   for (uint32_t VK = 0; VK <= IPVK_Last; VK++) {
1038*0fca6ea1SDimitry Andric     if (VK == IPVK_IndirectCallTarget || VK == IPVK_VTableTarget)
1039fe6060f1SDimitry Andric       continue;
1040*0fca6ea1SDimitry Andric     uint32_t NS = Func.getNumValueSites(VK);
1041fe6060f1SDimitry Andric     for (uint32_t S = 0; S < NS; S++) {
1042bdd1243dSDimitry Andric       DenseSet<uint64_t> SeenValues;
1043*0fca6ea1SDimitry Andric       for (const auto &V : Func.getValueArrayForSite(VK, S))
1044*0fca6ea1SDimitry Andric         if (!SeenValues.insert(V.Value).second)
1045fe6060f1SDimitry Andric           return make_error<InstrProfError>(instrprof_error::invalid_prof);
1046fe6060f1SDimitry Andric     }
1047fe6060f1SDimitry Andric   }
1048fe6060f1SDimitry Andric 
1049fe6060f1SDimitry Andric   return Error::success();
1050fe6060f1SDimitry Andric }
1051fe6060f1SDimitry Andric 
writeRecordInText(StringRef Name,uint64_t Hash,const InstrProfRecord & Func,InstrProfSymtab & Symtab,raw_fd_ostream & OS)10520b57cec5SDimitry Andric void InstrProfWriter::writeRecordInText(StringRef Name, uint64_t Hash,
10530b57cec5SDimitry Andric                                         const InstrProfRecord &Func,
10540b57cec5SDimitry Andric                                         InstrProfSymtab &Symtab,
10550b57cec5SDimitry Andric                                         raw_fd_ostream &OS) {
10560b57cec5SDimitry Andric   OS << Name << "\n";
10570b57cec5SDimitry Andric   OS << "# Func Hash:\n" << Hash << "\n";
10580b57cec5SDimitry Andric   OS << "# Num Counters:\n" << Func.Counts.size() << "\n";
10590b57cec5SDimitry Andric   OS << "# Counter Values:\n";
10600b57cec5SDimitry Andric   for (uint64_t Count : Func.Counts)
10610b57cec5SDimitry Andric     OS << Count << "\n";
10620b57cec5SDimitry Andric 
10635f757f3fSDimitry Andric   if (Func.BitmapBytes.size() > 0) {
10645f757f3fSDimitry Andric     OS << "# Num Bitmap Bytes:\n$" << Func.BitmapBytes.size() << "\n";
10655f757f3fSDimitry Andric     OS << "# Bitmap Byte Values:\n";
10665f757f3fSDimitry Andric     for (uint8_t Byte : Func.BitmapBytes) {
10675f757f3fSDimitry Andric       OS << "0x";
10685f757f3fSDimitry Andric       OS.write_hex(Byte);
10695f757f3fSDimitry Andric       OS << "\n";
10705f757f3fSDimitry Andric     }
10715f757f3fSDimitry Andric     OS << "\n";
10725f757f3fSDimitry Andric   }
10735f757f3fSDimitry Andric 
10740b57cec5SDimitry Andric   uint32_t NumValueKinds = Func.getNumValueKinds();
10750b57cec5SDimitry Andric   if (!NumValueKinds) {
10760b57cec5SDimitry Andric     OS << "\n";
10770b57cec5SDimitry Andric     return;
10780b57cec5SDimitry Andric   }
10790b57cec5SDimitry Andric 
10800b57cec5SDimitry Andric   OS << "# Num Value Kinds:\n" << Func.getNumValueKinds() << "\n";
10810b57cec5SDimitry Andric   for (uint32_t VK = 0; VK < IPVK_Last + 1; VK++) {
10820b57cec5SDimitry Andric     uint32_t NS = Func.getNumValueSites(VK);
10830b57cec5SDimitry Andric     if (!NS)
10840b57cec5SDimitry Andric       continue;
10850b57cec5SDimitry Andric     OS << "# ValueKind = " << ValueProfKindStr[VK] << ":\n" << VK << "\n";
10860b57cec5SDimitry Andric     OS << "# NumValueSites:\n" << NS << "\n";
10870b57cec5SDimitry Andric     for (uint32_t S = 0; S < NS; S++) {
1088*0fca6ea1SDimitry Andric       auto VD = Func.getValueArrayForSite(VK, S);
1089*0fca6ea1SDimitry Andric       OS << VD.size() << "\n";
1090*0fca6ea1SDimitry Andric       for (const auto &V : VD) {
1091*0fca6ea1SDimitry Andric         if (VK == IPVK_IndirectCallTarget || VK == IPVK_VTableTarget)
1092*0fca6ea1SDimitry Andric           OS << Symtab.getFuncOrVarNameIfDefined(V.Value) << ":" << V.Count
1093*0fca6ea1SDimitry Andric              << "\n";
10940b57cec5SDimitry Andric         else
1095*0fca6ea1SDimitry Andric           OS << V.Value << ":" << V.Count << "\n";
10960b57cec5SDimitry Andric       }
10970b57cec5SDimitry Andric     }
10980b57cec5SDimitry Andric   }
10990b57cec5SDimitry Andric 
11000b57cec5SDimitry Andric   OS << "\n";
11010b57cec5SDimitry Andric }
11020b57cec5SDimitry Andric 
writeText(raw_fd_ostream & OS)11030b57cec5SDimitry Andric Error InstrProfWriter::writeText(raw_fd_ostream &OS) {
11041fd87a68SDimitry Andric   // Check CS first since it implies an IR level profile.
110581ad6265SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::ContextSensitive))
11060b57cec5SDimitry Andric     OS << "# CSIR level Instrumentation Flag\n:csir\n";
110781ad6265SDimitry Andric   else if (static_cast<bool>(ProfileKind & InstrProfKind::IRInstrumentation))
11081fd87a68SDimitry Andric     OS << "# IR level Instrumentation Flag\n:ir\n";
11091fd87a68SDimitry Andric 
111081ad6265SDimitry Andric   if (static_cast<bool>(ProfileKind &
111181ad6265SDimitry Andric                         InstrProfKind::FunctionEntryInstrumentation))
1112e8d8bef9SDimitry Andric     OS << "# Always instrument the function entry block\n:entry_first\n";
11135f757f3fSDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::SingleByteCoverage))
11145f757f3fSDimitry Andric     OS << "# Instrument block coverage\n:single_byte_coverage\n";
11150b57cec5SDimitry Andric   InstrProfSymtab Symtab;
11160b57cec5SDimitry Andric 
11170b57cec5SDimitry Andric   using FuncPair = detail::DenseMapPair<uint64_t, InstrProfRecord>;
11180b57cec5SDimitry Andric   using RecordType = std::pair<StringRef, FuncPair>;
11190b57cec5SDimitry Andric   SmallVector<RecordType, 4> OrderedFuncData;
11200b57cec5SDimitry Andric 
11210b57cec5SDimitry Andric   for (const auto &I : FunctionData) {
11220b57cec5SDimitry Andric     if (shouldEncodeData(I.getValue())) {
11230b57cec5SDimitry Andric       if (Error E = Symtab.addFuncName(I.getKey()))
11240b57cec5SDimitry Andric         return E;
11250b57cec5SDimitry Andric       for (const auto &Func : I.getValue())
11260b57cec5SDimitry Andric         OrderedFuncData.push_back(std::make_pair(I.getKey(), Func));
11270b57cec5SDimitry Andric     }
11280b57cec5SDimitry Andric   }
11290b57cec5SDimitry Andric 
1130*0fca6ea1SDimitry Andric   for (const auto &VTableName : VTableNames)
1131*0fca6ea1SDimitry Andric     if (Error E = Symtab.addVTableName(VTableName.getKey()))
1132*0fca6ea1SDimitry Andric       return E;
1133*0fca6ea1SDimitry Andric 
113406c3fb27SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::TemporalProfile))
113506c3fb27SDimitry Andric     writeTextTemporalProfTraceData(OS, Symtab);
113606c3fb27SDimitry Andric 
11370b57cec5SDimitry Andric   llvm::sort(OrderedFuncData, [](const RecordType &A, const RecordType &B) {
11380b57cec5SDimitry Andric     return std::tie(A.first, A.second.first) <
11390b57cec5SDimitry Andric            std::tie(B.first, B.second.first);
11400b57cec5SDimitry Andric   });
11410b57cec5SDimitry Andric 
11420b57cec5SDimitry Andric   for (const auto &record : OrderedFuncData) {
11430b57cec5SDimitry Andric     const StringRef &Name = record.first;
11440b57cec5SDimitry Andric     const FuncPair &Func = record.second;
11450b57cec5SDimitry Andric     writeRecordInText(Name, Func.first, Func.second, Symtab, OS);
11460b57cec5SDimitry Andric   }
11470b57cec5SDimitry Andric 
1148fe6060f1SDimitry Andric   for (const auto &record : OrderedFuncData) {
1149fe6060f1SDimitry Andric     const FuncPair &Func = record.second;
1150fe6060f1SDimitry Andric     if (Error E = validateRecord(Func.second))
1151fe6060f1SDimitry Andric       return E;
1152fe6060f1SDimitry Andric   }
1153fe6060f1SDimitry Andric 
11540b57cec5SDimitry Andric   return Error::success();
11550b57cec5SDimitry Andric }
115606c3fb27SDimitry Andric 
writeTextTemporalProfTraceData(raw_fd_ostream & OS,InstrProfSymtab & Symtab)115706c3fb27SDimitry Andric void InstrProfWriter::writeTextTemporalProfTraceData(raw_fd_ostream &OS,
115806c3fb27SDimitry Andric                                                      InstrProfSymtab &Symtab) {
115906c3fb27SDimitry Andric   OS << ":temporal_prof_traces\n";
116006c3fb27SDimitry Andric   OS << "# Num Temporal Profile Traces:\n" << TemporalProfTraces.size() << "\n";
116106c3fb27SDimitry Andric   OS << "# Temporal Profile Trace Stream Size:\n"
116206c3fb27SDimitry Andric      << TemporalProfTraceStreamSize << "\n";
116306c3fb27SDimitry Andric   for (auto &Trace : TemporalProfTraces) {
116406c3fb27SDimitry Andric     OS << "# Weight:\n" << Trace.Weight << "\n";
116506c3fb27SDimitry Andric     for (auto &NameRef : Trace.FunctionNameRefs)
11665f757f3fSDimitry Andric       OS << Symtab.getFuncOrVarName(NameRef) << ",";
116706c3fb27SDimitry Andric     OS << "\n";
116806c3fb27SDimitry Andric   }
116906c3fb27SDimitry Andric   OS << "\n";
117006c3fb27SDimitry Andric }
1171