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"
16*fe013be4SDimitry 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"
220b57cec5SDimitry Andric #include "llvm/Support/Endian.h"
230b57cec5SDimitry Andric #include "llvm/Support/EndianStream.h"
240b57cec5SDimitry Andric #include "llvm/Support/Error.h"
250b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
260b57cec5SDimitry Andric #include "llvm/Support/OnDiskHashTable.h"
270b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
280b57cec5SDimitry Andric #include <cstdint>
290b57cec5SDimitry Andric #include <memory>
300b57cec5SDimitry Andric #include <string>
310b57cec5SDimitry Andric #include <tuple>
320b57cec5SDimitry Andric #include <utility>
330b57cec5SDimitry Andric #include <vector>
340b57cec5SDimitry Andric 
350b57cec5SDimitry Andric using namespace llvm;
360b57cec5SDimitry Andric 
370b57cec5SDimitry Andric // A struct to define how the data stream should be patched. For Indexed
380b57cec5SDimitry Andric // profiling, only uint64_t data type is needed.
390b57cec5SDimitry Andric struct PatchItem {
400b57cec5SDimitry Andric   uint64_t Pos; // Where to patch.
410b57cec5SDimitry Andric   uint64_t *D;  // Pointer to an array of source data.
420b57cec5SDimitry Andric   int N;        // Number of elements in \c D array.
430b57cec5SDimitry Andric };
440b57cec5SDimitry Andric 
450b57cec5SDimitry Andric namespace llvm {
460b57cec5SDimitry Andric 
470b57cec5SDimitry Andric // A wrapper class to abstract writer stream with support of bytes
480b57cec5SDimitry Andric // back patching.
490b57cec5SDimitry Andric class ProfOStream {
500b57cec5SDimitry Andric public:
510b57cec5SDimitry Andric   ProfOStream(raw_fd_ostream &FD)
520b57cec5SDimitry Andric       : IsFDOStream(true), OS(FD), LE(FD, support::little) {}
530b57cec5SDimitry Andric   ProfOStream(raw_string_ostream &STR)
540b57cec5SDimitry Andric       : IsFDOStream(false), OS(STR), LE(STR, support::little) {}
550b57cec5SDimitry Andric 
560b57cec5SDimitry Andric   uint64_t tell() { return OS.tell(); }
570b57cec5SDimitry Andric   void write(uint64_t V) { LE.write<uint64_t>(V); }
58bdd1243dSDimitry Andric   void writeByte(uint8_t V) { LE.write<uint8_t>(V); }
590b57cec5SDimitry Andric 
600b57cec5SDimitry Andric   // \c patch can only be called when all data is written and flushed.
610b57cec5SDimitry Andric   // For raw_string_ostream, the patch is done on the target string
620b57cec5SDimitry Andric   // directly and it won't be reflected in the stream's internal buffer.
630b57cec5SDimitry Andric   void patch(PatchItem *P, int NItems) {
640b57cec5SDimitry Andric     using namespace support;
650b57cec5SDimitry Andric 
660b57cec5SDimitry Andric     if (IsFDOStream) {
670b57cec5SDimitry Andric       raw_fd_ostream &FDOStream = static_cast<raw_fd_ostream &>(OS);
6881ad6265SDimitry Andric       const uint64_t LastPos = FDOStream.tell();
690b57cec5SDimitry Andric       for (int K = 0; K < NItems; K++) {
700b57cec5SDimitry Andric         FDOStream.seek(P[K].Pos);
710b57cec5SDimitry Andric         for (int I = 0; I < P[K].N; I++)
720b57cec5SDimitry Andric           write(P[K].D[I]);
730b57cec5SDimitry Andric       }
7481ad6265SDimitry Andric       // Reset the stream to the last position after patching so that users
7581ad6265SDimitry Andric       // don't accidentally overwrite data. This makes it consistent with
7681ad6265SDimitry Andric       // the string stream below which replaces the data directly.
7781ad6265SDimitry Andric       FDOStream.seek(LastPos);
780b57cec5SDimitry Andric     } else {
790b57cec5SDimitry Andric       raw_string_ostream &SOStream = static_cast<raw_string_ostream &>(OS);
800b57cec5SDimitry Andric       std::string &Data = SOStream.str(); // with flush
810b57cec5SDimitry Andric       for (int K = 0; K < NItems; K++) {
820b57cec5SDimitry Andric         for (int I = 0; I < P[K].N; I++) {
830b57cec5SDimitry Andric           uint64_t Bytes = endian::byte_swap<uint64_t, little>(P[K].D[I]);
840b57cec5SDimitry Andric           Data.replace(P[K].Pos + I * sizeof(uint64_t), sizeof(uint64_t),
850b57cec5SDimitry Andric                        (const char *)&Bytes, sizeof(uint64_t));
860b57cec5SDimitry Andric         }
870b57cec5SDimitry Andric       }
880b57cec5SDimitry Andric     }
890b57cec5SDimitry Andric   }
900b57cec5SDimitry Andric 
910b57cec5SDimitry Andric   // If \c OS is an instance of \c raw_fd_ostream, this field will be
920b57cec5SDimitry Andric   // true. Otherwise, \c OS will be an raw_string_ostream.
930b57cec5SDimitry Andric   bool IsFDOStream;
940b57cec5SDimitry Andric   raw_ostream &OS;
950b57cec5SDimitry Andric   support::endian::Writer LE;
960b57cec5SDimitry Andric };
970b57cec5SDimitry Andric 
980b57cec5SDimitry Andric class InstrProfRecordWriterTrait {
990b57cec5SDimitry Andric public:
1000b57cec5SDimitry Andric   using key_type = StringRef;
1010b57cec5SDimitry Andric   using key_type_ref = StringRef;
1020b57cec5SDimitry Andric 
1030b57cec5SDimitry Andric   using data_type = const InstrProfWriter::ProfilingData *const;
1040b57cec5SDimitry Andric   using data_type_ref = const InstrProfWriter::ProfilingData *const;
1050b57cec5SDimitry Andric 
1060b57cec5SDimitry Andric   using hash_value_type = uint64_t;
1070b57cec5SDimitry Andric   using offset_type = uint64_t;
1080b57cec5SDimitry Andric 
1090b57cec5SDimitry Andric   support::endianness ValueProfDataEndianness = support::little;
1100b57cec5SDimitry Andric   InstrProfSummaryBuilder *SummaryBuilder;
1110b57cec5SDimitry Andric   InstrProfSummaryBuilder *CSSummaryBuilder;
1120b57cec5SDimitry Andric 
1130b57cec5SDimitry Andric   InstrProfRecordWriterTrait() = default;
1140b57cec5SDimitry Andric 
1150b57cec5SDimitry Andric   static hash_value_type ComputeHash(key_type_ref K) {
1160b57cec5SDimitry Andric     return IndexedInstrProf::ComputeHash(K);
1170b57cec5SDimitry Andric   }
1180b57cec5SDimitry Andric 
1190b57cec5SDimitry Andric   static std::pair<offset_type, offset_type>
1200b57cec5SDimitry Andric   EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) {
1210b57cec5SDimitry Andric     using namespace support;
1220b57cec5SDimitry Andric 
1230b57cec5SDimitry Andric     endian::Writer LE(Out, little);
1240b57cec5SDimitry Andric 
1250b57cec5SDimitry Andric     offset_type N = K.size();
1260b57cec5SDimitry Andric     LE.write<offset_type>(N);
1270b57cec5SDimitry Andric 
1280b57cec5SDimitry Andric     offset_type M = 0;
1290b57cec5SDimitry Andric     for (const auto &ProfileData : *V) {
1300b57cec5SDimitry Andric       const InstrProfRecord &ProfRecord = ProfileData.second;
1310b57cec5SDimitry Andric       M += sizeof(uint64_t); // The function hash
1320b57cec5SDimitry Andric       M += sizeof(uint64_t); // The size of the Counts vector
1330b57cec5SDimitry Andric       M += ProfRecord.Counts.size() * sizeof(uint64_t);
1340b57cec5SDimitry Andric 
1350b57cec5SDimitry Andric       // Value data
1360b57cec5SDimitry Andric       M += ValueProfData::getSize(ProfileData.second);
1370b57cec5SDimitry Andric     }
1380b57cec5SDimitry Andric     LE.write<offset_type>(M);
1390b57cec5SDimitry Andric 
1400b57cec5SDimitry Andric     return std::make_pair(N, M);
1410b57cec5SDimitry Andric   }
1420b57cec5SDimitry Andric 
1430b57cec5SDimitry Andric   void EmitKey(raw_ostream &Out, key_type_ref K, offset_type N) {
1440b57cec5SDimitry Andric     Out.write(K.data(), N);
1450b57cec5SDimitry Andric   }
1460b57cec5SDimitry Andric 
1470b57cec5SDimitry Andric   void EmitData(raw_ostream &Out, key_type_ref, data_type_ref V, offset_type) {
1480b57cec5SDimitry Andric     using namespace support;
1490b57cec5SDimitry Andric 
1500b57cec5SDimitry Andric     endian::Writer LE(Out, little);
1510b57cec5SDimitry Andric     for (const auto &ProfileData : *V) {
1520b57cec5SDimitry Andric       const InstrProfRecord &ProfRecord = ProfileData.second;
1530b57cec5SDimitry Andric       if (NamedInstrProfRecord::hasCSFlagInHash(ProfileData.first))
1540b57cec5SDimitry Andric         CSSummaryBuilder->addRecord(ProfRecord);
1550b57cec5SDimitry Andric       else
1560b57cec5SDimitry Andric         SummaryBuilder->addRecord(ProfRecord);
1570b57cec5SDimitry Andric 
1580b57cec5SDimitry Andric       LE.write<uint64_t>(ProfileData.first); // Function hash
1590b57cec5SDimitry Andric       LE.write<uint64_t>(ProfRecord.Counts.size());
1600b57cec5SDimitry Andric       for (uint64_t I : ProfRecord.Counts)
1610b57cec5SDimitry Andric         LE.write<uint64_t>(I);
1620b57cec5SDimitry Andric 
1630b57cec5SDimitry Andric       // Write value data
1640b57cec5SDimitry Andric       std::unique_ptr<ValueProfData> VDataPtr =
1650b57cec5SDimitry Andric           ValueProfData::serializeFrom(ProfileData.second);
1660b57cec5SDimitry Andric       uint32_t S = VDataPtr->getSize();
1670b57cec5SDimitry Andric       VDataPtr->swapBytesFromHost(ValueProfDataEndianness);
1680b57cec5SDimitry Andric       Out.write((const char *)VDataPtr.get(), S);
1690b57cec5SDimitry Andric     }
1700b57cec5SDimitry Andric   }
1710b57cec5SDimitry Andric };
1720b57cec5SDimitry Andric 
1730b57cec5SDimitry Andric } // end namespace llvm
1740b57cec5SDimitry Andric 
175*fe013be4SDimitry Andric InstrProfWriter::InstrProfWriter(bool Sparse,
176*fe013be4SDimitry Andric                                  uint64_t TemporalProfTraceReservoirSize,
177*fe013be4SDimitry Andric                                  uint64_t MaxTemporalProfTraceLength)
178*fe013be4SDimitry Andric     : Sparse(Sparse), MaxTemporalProfTraceLength(MaxTemporalProfTraceLength),
179*fe013be4SDimitry Andric       TemporalProfTraceReservoirSize(TemporalProfTraceReservoirSize),
180*fe013be4SDimitry Andric       InfoObj(new InstrProfRecordWriterTrait()) {}
1810b57cec5SDimitry Andric 
1820b57cec5SDimitry Andric InstrProfWriter::~InstrProfWriter() { delete InfoObj; }
1830b57cec5SDimitry Andric 
1840b57cec5SDimitry Andric // Internal interface for testing purpose only.
1850b57cec5SDimitry Andric void InstrProfWriter::setValueProfDataEndianness(
1860b57cec5SDimitry Andric     support::endianness Endianness) {
1870b57cec5SDimitry Andric   InfoObj->ValueProfDataEndianness = Endianness;
1880b57cec5SDimitry Andric }
1890b57cec5SDimitry Andric 
1900b57cec5SDimitry Andric void InstrProfWriter::setOutputSparse(bool Sparse) {
1910b57cec5SDimitry Andric   this->Sparse = Sparse;
1920b57cec5SDimitry Andric }
1930b57cec5SDimitry Andric 
1940b57cec5SDimitry Andric void InstrProfWriter::addRecord(NamedInstrProfRecord &&I, uint64_t Weight,
1950b57cec5SDimitry Andric                                 function_ref<void(Error)> Warn) {
1960b57cec5SDimitry Andric   auto Name = I.Name;
1970b57cec5SDimitry Andric   auto Hash = I.Hash;
1980b57cec5SDimitry Andric   addRecord(Name, Hash, std::move(I), Weight, Warn);
1990b57cec5SDimitry Andric }
2000b57cec5SDimitry Andric 
2010b57cec5SDimitry Andric void InstrProfWriter::overlapRecord(NamedInstrProfRecord &&Other,
2020b57cec5SDimitry Andric                                     OverlapStats &Overlap,
2030b57cec5SDimitry Andric                                     OverlapStats &FuncLevelOverlap,
2040b57cec5SDimitry Andric                                     const OverlapFuncFilters &FuncFilter) {
2050b57cec5SDimitry Andric   auto Name = Other.Name;
2060b57cec5SDimitry Andric   auto Hash = Other.Hash;
2078bcb0991SDimitry Andric   Other.accumulateCounts(FuncLevelOverlap.Test);
208*fe013be4SDimitry Andric   if (!FunctionData.contains(Name)) {
2090b57cec5SDimitry Andric     Overlap.addOneUnique(FuncLevelOverlap.Test);
2100b57cec5SDimitry Andric     return;
2110b57cec5SDimitry Andric   }
2120b57cec5SDimitry Andric   if (FuncLevelOverlap.Test.CountSum < 1.0f) {
2130b57cec5SDimitry Andric     Overlap.Overlap.NumEntries += 1;
2140b57cec5SDimitry Andric     return;
2150b57cec5SDimitry Andric   }
2160b57cec5SDimitry Andric   auto &ProfileDataMap = FunctionData[Name];
2170b57cec5SDimitry Andric   bool NewFunc;
2180b57cec5SDimitry Andric   ProfilingData::iterator Where;
2190b57cec5SDimitry Andric   std::tie(Where, NewFunc) =
2200b57cec5SDimitry Andric       ProfileDataMap.insert(std::make_pair(Hash, InstrProfRecord()));
2210b57cec5SDimitry Andric   if (NewFunc) {
2220b57cec5SDimitry Andric     Overlap.addOneMismatch(FuncLevelOverlap.Test);
2230b57cec5SDimitry Andric     return;
2240b57cec5SDimitry Andric   }
2250b57cec5SDimitry Andric   InstrProfRecord &Dest = Where->second;
2260b57cec5SDimitry Andric 
2270b57cec5SDimitry Andric   uint64_t ValueCutoff = FuncFilter.ValueCutoff;
228349cc55cSDimitry Andric   if (!FuncFilter.NameFilter.empty() && Name.contains(FuncFilter.NameFilter))
2290b57cec5SDimitry Andric     ValueCutoff = 0;
2300b57cec5SDimitry Andric 
2310b57cec5SDimitry Andric   Dest.overlap(Other, Overlap, FuncLevelOverlap, ValueCutoff);
2320b57cec5SDimitry Andric }
2330b57cec5SDimitry Andric 
2340b57cec5SDimitry Andric void InstrProfWriter::addRecord(StringRef Name, uint64_t Hash,
2350b57cec5SDimitry Andric                                 InstrProfRecord &&I, uint64_t Weight,
2360b57cec5SDimitry Andric                                 function_ref<void(Error)> Warn) {
2370b57cec5SDimitry Andric   auto &ProfileDataMap = FunctionData[Name];
2380b57cec5SDimitry Andric 
2390b57cec5SDimitry Andric   bool NewFunc;
2400b57cec5SDimitry Andric   ProfilingData::iterator Where;
2410b57cec5SDimitry Andric   std::tie(Where, NewFunc) =
2420b57cec5SDimitry Andric       ProfileDataMap.insert(std::make_pair(Hash, InstrProfRecord()));
2430b57cec5SDimitry Andric   InstrProfRecord &Dest = Where->second;
2440b57cec5SDimitry Andric 
2450b57cec5SDimitry Andric   auto MapWarn = [&](instrprof_error E) {
2460b57cec5SDimitry Andric     Warn(make_error<InstrProfError>(E));
2470b57cec5SDimitry Andric   };
2480b57cec5SDimitry Andric 
2490b57cec5SDimitry Andric   if (NewFunc) {
2500b57cec5SDimitry Andric     // We've never seen a function with this name and hash, add it.
2510b57cec5SDimitry Andric     Dest = std::move(I);
2520b57cec5SDimitry Andric     if (Weight > 1)
253e8d8bef9SDimitry Andric       Dest.scale(Weight, 1, MapWarn);
2540b57cec5SDimitry Andric   } else {
2550b57cec5SDimitry Andric     // We're updating a function we've seen before.
2560b57cec5SDimitry Andric     Dest.merge(I, Weight, MapWarn);
2570b57cec5SDimitry Andric   }
2580b57cec5SDimitry Andric 
2590b57cec5SDimitry Andric   Dest.sortValueData();
2600b57cec5SDimitry Andric }
2610b57cec5SDimitry Andric 
26281ad6265SDimitry Andric void InstrProfWriter::addMemProfRecord(
26381ad6265SDimitry Andric     const Function::GUID Id, const memprof::IndexedMemProfRecord &Record) {
26481ad6265SDimitry Andric   auto Result = MemProfRecordData.insert({Id, Record});
26581ad6265SDimitry Andric   // If we inserted a new record then we are done.
26681ad6265SDimitry Andric   if (Result.second) {
26781ad6265SDimitry Andric     return;
26881ad6265SDimitry Andric   }
26981ad6265SDimitry Andric   memprof::IndexedMemProfRecord &Existing = Result.first->second;
27081ad6265SDimitry Andric   Existing.merge(Record);
27181ad6265SDimitry Andric }
27281ad6265SDimitry Andric 
27381ad6265SDimitry Andric bool InstrProfWriter::addMemProfFrame(const memprof::FrameId Id,
27481ad6265SDimitry Andric                                       const memprof::Frame &Frame,
27581ad6265SDimitry Andric                                       function_ref<void(Error)> Warn) {
27681ad6265SDimitry Andric   auto Result = MemProfFrameData.insert({Id, Frame});
27781ad6265SDimitry Andric   // If a mapping already exists for the current frame id and it does not
27881ad6265SDimitry Andric   // match the new mapping provided then reset the existing contents and bail
27981ad6265SDimitry Andric   // out. We don't support the merging of memprof data whose Frame -> Id
28081ad6265SDimitry Andric   // mapping across profiles is inconsistent.
28181ad6265SDimitry Andric   if (!Result.second && Result.first->second != Frame) {
28281ad6265SDimitry Andric     Warn(make_error<InstrProfError>(instrprof_error::malformed,
28381ad6265SDimitry Andric                                     "frame to id mapping mismatch"));
28481ad6265SDimitry Andric     return false;
28581ad6265SDimitry Andric   }
28681ad6265SDimitry Andric   return true;
28781ad6265SDimitry Andric }
28881ad6265SDimitry Andric 
289bdd1243dSDimitry Andric void InstrProfWriter::addBinaryIds(ArrayRef<llvm::object::BuildID> BIs) {
290bdd1243dSDimitry Andric   llvm::append_range(BinaryIds, BIs);
291bdd1243dSDimitry Andric }
292bdd1243dSDimitry Andric 
293*fe013be4SDimitry Andric void InstrProfWriter::addTemporalProfileTrace(TemporalProfTraceTy Trace) {
294*fe013be4SDimitry Andric   if (Trace.FunctionNameRefs.size() > MaxTemporalProfTraceLength)
295*fe013be4SDimitry Andric     Trace.FunctionNameRefs.resize(MaxTemporalProfTraceLength);
296*fe013be4SDimitry Andric   if (Trace.FunctionNameRefs.empty())
297*fe013be4SDimitry Andric     return;
298*fe013be4SDimitry Andric 
299*fe013be4SDimitry Andric   if (TemporalProfTraceStreamSize < TemporalProfTraceReservoirSize) {
300*fe013be4SDimitry Andric     // Simply append the trace if we have not yet hit our reservoir size limit.
301*fe013be4SDimitry Andric     TemporalProfTraces.push_back(std::move(Trace));
302*fe013be4SDimitry Andric   } else {
303*fe013be4SDimitry Andric     // Otherwise, replace a random trace in the stream.
304*fe013be4SDimitry Andric     std::uniform_int_distribution<uint64_t> Distribution(
305*fe013be4SDimitry Andric         0, TemporalProfTraceStreamSize);
306*fe013be4SDimitry Andric     uint64_t RandomIndex = Distribution(RNG);
307*fe013be4SDimitry Andric     if (RandomIndex < TemporalProfTraces.size())
308*fe013be4SDimitry Andric       TemporalProfTraces[RandomIndex] = std::move(Trace);
309*fe013be4SDimitry Andric   }
310*fe013be4SDimitry Andric   ++TemporalProfTraceStreamSize;
311*fe013be4SDimitry Andric }
312*fe013be4SDimitry Andric 
313*fe013be4SDimitry Andric void InstrProfWriter::addTemporalProfileTraces(
314*fe013be4SDimitry Andric     SmallVectorImpl<TemporalProfTraceTy> &SrcTraces, uint64_t SrcStreamSize) {
315*fe013be4SDimitry Andric   // Assume that the source has the same reservoir size as the destination to
316*fe013be4SDimitry Andric   // avoid needing to record it in the indexed profile format.
317*fe013be4SDimitry Andric   bool IsDestSampled =
318*fe013be4SDimitry Andric       (TemporalProfTraceStreamSize > TemporalProfTraceReservoirSize);
319*fe013be4SDimitry Andric   bool IsSrcSampled = (SrcStreamSize > TemporalProfTraceReservoirSize);
320*fe013be4SDimitry Andric   if (!IsDestSampled && IsSrcSampled) {
321*fe013be4SDimitry Andric     // If one of the traces are sampled, ensure that it belongs to Dest.
322*fe013be4SDimitry Andric     std::swap(TemporalProfTraces, SrcTraces);
323*fe013be4SDimitry Andric     std::swap(TemporalProfTraceStreamSize, SrcStreamSize);
324*fe013be4SDimitry Andric     std::swap(IsDestSampled, IsSrcSampled);
325*fe013be4SDimitry Andric   }
326*fe013be4SDimitry Andric   if (!IsSrcSampled) {
327*fe013be4SDimitry Andric     // If the source stream is not sampled, we add each source trace normally.
328*fe013be4SDimitry Andric     for (auto &Trace : SrcTraces)
329*fe013be4SDimitry Andric       addTemporalProfileTrace(std::move(Trace));
330*fe013be4SDimitry Andric     return;
331*fe013be4SDimitry Andric   }
332*fe013be4SDimitry Andric   // Otherwise, we find the traces that would have been removed if we added
333*fe013be4SDimitry Andric   // the whole source stream.
334*fe013be4SDimitry Andric   SmallSetVector<uint64_t, 8> IndicesToReplace;
335*fe013be4SDimitry Andric   for (uint64_t I = 0; I < SrcStreamSize; I++) {
336*fe013be4SDimitry Andric     std::uniform_int_distribution<uint64_t> Distribution(
337*fe013be4SDimitry Andric         0, TemporalProfTraceStreamSize);
338*fe013be4SDimitry Andric     uint64_t RandomIndex = Distribution(RNG);
339*fe013be4SDimitry Andric     if (RandomIndex < TemporalProfTraces.size())
340*fe013be4SDimitry Andric       IndicesToReplace.insert(RandomIndex);
341*fe013be4SDimitry Andric     ++TemporalProfTraceStreamSize;
342*fe013be4SDimitry Andric   }
343*fe013be4SDimitry Andric   // Then we insert a random sample of the source traces.
344*fe013be4SDimitry Andric   llvm::shuffle(SrcTraces.begin(), SrcTraces.end(), RNG);
345*fe013be4SDimitry Andric   for (const auto &[Index, Trace] : llvm::zip(IndicesToReplace, SrcTraces))
346*fe013be4SDimitry Andric     TemporalProfTraces[Index] = std::move(Trace);
347*fe013be4SDimitry Andric }
348*fe013be4SDimitry Andric 
3490b57cec5SDimitry Andric void InstrProfWriter::mergeRecordsFromWriter(InstrProfWriter &&IPW,
3500b57cec5SDimitry Andric                                              function_ref<void(Error)> Warn) {
3510b57cec5SDimitry Andric   for (auto &I : IPW.FunctionData)
3520b57cec5SDimitry Andric     for (auto &Func : I.getValue())
3530b57cec5SDimitry Andric       addRecord(I.getKey(), Func.first, std::move(Func.second), 1, Warn);
35481ad6265SDimitry Andric 
355bdd1243dSDimitry Andric   BinaryIds.reserve(BinaryIds.size() + IPW.BinaryIds.size());
356bdd1243dSDimitry Andric   for (auto &I : IPW.BinaryIds)
357bdd1243dSDimitry Andric     addBinaryIds(I);
358bdd1243dSDimitry Andric 
359*fe013be4SDimitry Andric   addTemporalProfileTraces(IPW.TemporalProfTraces,
360*fe013be4SDimitry Andric                            IPW.TemporalProfTraceStreamSize);
361*fe013be4SDimitry Andric 
36281ad6265SDimitry Andric   MemProfFrameData.reserve(IPW.MemProfFrameData.size());
36381ad6265SDimitry Andric   for (auto &I : IPW.MemProfFrameData) {
36481ad6265SDimitry Andric     // If we weren't able to add the frame mappings then it doesn't make sense
36581ad6265SDimitry Andric     // to try to merge the records from this profile.
36681ad6265SDimitry Andric     if (!addMemProfFrame(I.first, I.second, Warn))
36781ad6265SDimitry Andric       return;
36881ad6265SDimitry Andric   }
36981ad6265SDimitry Andric 
37081ad6265SDimitry Andric   MemProfRecordData.reserve(IPW.MemProfRecordData.size());
37181ad6265SDimitry Andric   for (auto &I : IPW.MemProfRecordData) {
37281ad6265SDimitry Andric     addMemProfRecord(I.first, I.second);
37381ad6265SDimitry Andric   }
3740b57cec5SDimitry Andric }
3750b57cec5SDimitry Andric 
3760b57cec5SDimitry Andric bool InstrProfWriter::shouldEncodeData(const ProfilingData &PD) {
3770b57cec5SDimitry Andric   if (!Sparse)
3780b57cec5SDimitry Andric     return true;
3790b57cec5SDimitry Andric   for (const auto &Func : PD) {
3800b57cec5SDimitry Andric     const InstrProfRecord &IPR = Func.second;
3810b57cec5SDimitry Andric     if (llvm::any_of(IPR.Counts, [](uint64_t Count) { return Count > 0; }))
3820b57cec5SDimitry Andric       return true;
3830b57cec5SDimitry Andric   }
3840b57cec5SDimitry Andric   return false;
3850b57cec5SDimitry Andric }
3860b57cec5SDimitry Andric 
3870b57cec5SDimitry Andric static void setSummary(IndexedInstrProf::Summary *TheSummary,
3880b57cec5SDimitry Andric                        ProfileSummary &PS) {
3890b57cec5SDimitry Andric   using namespace IndexedInstrProf;
3900b57cec5SDimitry Andric 
391349cc55cSDimitry Andric   const std::vector<ProfileSummaryEntry> &Res = PS.getDetailedSummary();
3920b57cec5SDimitry Andric   TheSummary->NumSummaryFields = Summary::NumKinds;
3930b57cec5SDimitry Andric   TheSummary->NumCutoffEntries = Res.size();
3940b57cec5SDimitry Andric   TheSummary->set(Summary::MaxFunctionCount, PS.getMaxFunctionCount());
3950b57cec5SDimitry Andric   TheSummary->set(Summary::MaxBlockCount, PS.getMaxCount());
3960b57cec5SDimitry Andric   TheSummary->set(Summary::MaxInternalBlockCount, PS.getMaxInternalCount());
3970b57cec5SDimitry Andric   TheSummary->set(Summary::TotalBlockCount, PS.getTotalCount());
3980b57cec5SDimitry Andric   TheSummary->set(Summary::TotalNumBlocks, PS.getNumCounts());
3990b57cec5SDimitry Andric   TheSummary->set(Summary::TotalNumFunctions, PS.getNumFunctions());
4000b57cec5SDimitry Andric   for (unsigned I = 0; I < Res.size(); I++)
4010b57cec5SDimitry Andric     TheSummary->setEntry(I, Res[I]);
4020b57cec5SDimitry Andric }
4030b57cec5SDimitry Andric 
404fe6060f1SDimitry Andric Error InstrProfWriter::writeImpl(ProfOStream &OS) {
4050b57cec5SDimitry Andric   using namespace IndexedInstrProf;
406bdd1243dSDimitry Andric   using namespace support;
4070b57cec5SDimitry Andric 
4080b57cec5SDimitry Andric   OnDiskChainedHashTableGenerator<InstrProfRecordWriterTrait> Generator;
4090b57cec5SDimitry Andric 
4100b57cec5SDimitry Andric   InstrProfSummaryBuilder ISB(ProfileSummaryBuilder::DefaultCutoffs);
4110b57cec5SDimitry Andric   InfoObj->SummaryBuilder = &ISB;
4120b57cec5SDimitry Andric   InstrProfSummaryBuilder CSISB(ProfileSummaryBuilder::DefaultCutoffs);
4130b57cec5SDimitry Andric   InfoObj->CSSummaryBuilder = &CSISB;
4140b57cec5SDimitry Andric 
4150b57cec5SDimitry Andric   // Populate the hash table generator.
416*fe013be4SDimitry Andric   SmallVector<std::pair<StringRef, const ProfilingData *>, 0> OrderedData;
4170b57cec5SDimitry Andric   for (const auto &I : FunctionData)
4180b57cec5SDimitry Andric     if (shouldEncodeData(I.getValue()))
419*fe013be4SDimitry Andric       OrderedData.emplace_back((I.getKey()), &I.getValue());
420*fe013be4SDimitry Andric   llvm::sort(OrderedData, less_first());
421*fe013be4SDimitry Andric   for (const auto &I : OrderedData)
422*fe013be4SDimitry Andric     Generator.insert(I.first, I.second);
42381ad6265SDimitry Andric 
4240b57cec5SDimitry Andric   // Write the header.
4250b57cec5SDimitry Andric   IndexedInstrProf::Header Header;
4260b57cec5SDimitry Andric   Header.Magic = IndexedInstrProf::Magic;
4270b57cec5SDimitry Andric   Header.Version = IndexedInstrProf::ProfVersion::CurrentVersion;
42881ad6265SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::IRInstrumentation))
4290b57cec5SDimitry Andric     Header.Version |= VARIANT_MASK_IR_PROF;
43081ad6265SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::ContextSensitive))
4310b57cec5SDimitry Andric     Header.Version |= VARIANT_MASK_CSIR_PROF;
43281ad6265SDimitry Andric   if (static_cast<bool>(ProfileKind &
43381ad6265SDimitry Andric                         InstrProfKind::FunctionEntryInstrumentation))
434e8d8bef9SDimitry Andric     Header.Version |= VARIANT_MASK_INSTR_ENTRY;
4351fd87a68SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::SingleByteCoverage))
4361fd87a68SDimitry Andric     Header.Version |= VARIANT_MASK_BYTE_COVERAGE;
4371fd87a68SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::FunctionEntryOnly))
4381fd87a68SDimitry Andric     Header.Version |= VARIANT_MASK_FUNCTION_ENTRY_ONLY;
43981ad6265SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::MemProf))
44081ad6265SDimitry Andric     Header.Version |= VARIANT_MASK_MEMPROF;
441*fe013be4SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::TemporalProfile))
442*fe013be4SDimitry Andric     Header.Version |= VARIANT_MASK_TEMPORAL_PROF;
443e8d8bef9SDimitry Andric 
4440b57cec5SDimitry Andric   Header.Unused = 0;
4450b57cec5SDimitry Andric   Header.HashType = static_cast<uint64_t>(IndexedInstrProf::HashType);
4460b57cec5SDimitry Andric   Header.HashOffset = 0;
44781ad6265SDimitry Andric   Header.MemProfOffset = 0;
448bdd1243dSDimitry Andric   Header.BinaryIdOffset = 0;
449*fe013be4SDimitry Andric   Header.TemporalProfTracesOffset = 0;
4500b57cec5SDimitry Andric   int N = sizeof(IndexedInstrProf::Header) / sizeof(uint64_t);
4510b57cec5SDimitry Andric 
452*fe013be4SDimitry Andric   // Only write out all the fields except 'HashOffset', 'MemProfOffset',
453*fe013be4SDimitry Andric   // 'BinaryIdOffset' and `TemporalProfTracesOffset`. We need to remember the
454*fe013be4SDimitry Andric   // offset of these fields to allow back patching later.
455*fe013be4SDimitry Andric   for (int I = 0; I < N - 4; I++)
4560b57cec5SDimitry Andric     OS.write(reinterpret_cast<uint64_t *>(&Header)[I]);
4570b57cec5SDimitry Andric 
4580b57cec5SDimitry Andric   // Save the location of Header.HashOffset field in \c OS.
4590b57cec5SDimitry Andric   uint64_t HashTableStartFieldOffset = OS.tell();
4600b57cec5SDimitry Andric   // Reserve the space for HashOffset field.
4610b57cec5SDimitry Andric   OS.write(0);
4620b57cec5SDimitry Andric 
46381ad6265SDimitry Andric   // Save the location of MemProf profile data. This is stored in two parts as
46481ad6265SDimitry Andric   // the schema and as a separate on-disk chained hashtable.
46581ad6265SDimitry Andric   uint64_t MemProfSectionOffset = OS.tell();
46681ad6265SDimitry Andric   // Reserve space for the MemProf table field to be patched later if this
46781ad6265SDimitry Andric   // profile contains memory profile information.
46881ad6265SDimitry Andric   OS.write(0);
46981ad6265SDimitry Andric 
470bdd1243dSDimitry Andric   // Save the location of binary ids section.
471bdd1243dSDimitry Andric   uint64_t BinaryIdSectionOffset = OS.tell();
472bdd1243dSDimitry Andric   // Reserve space for the BinaryIdOffset field to be patched later if this
473bdd1243dSDimitry Andric   // profile contains binary ids.
474bdd1243dSDimitry Andric   OS.write(0);
475bdd1243dSDimitry Andric 
476*fe013be4SDimitry Andric   uint64_t TemporalProfTracesOffset = OS.tell();
477*fe013be4SDimitry Andric   OS.write(0);
478*fe013be4SDimitry Andric 
4790b57cec5SDimitry Andric   // Reserve space to write profile summary data.
4800b57cec5SDimitry Andric   uint32_t NumEntries = ProfileSummaryBuilder::DefaultCutoffs.size();
4810b57cec5SDimitry Andric   uint32_t SummarySize = Summary::getSize(Summary::NumKinds, NumEntries);
4820b57cec5SDimitry Andric   // Remember the summary offset.
4830b57cec5SDimitry Andric   uint64_t SummaryOffset = OS.tell();
4840b57cec5SDimitry Andric   for (unsigned I = 0; I < SummarySize / sizeof(uint64_t); I++)
4850b57cec5SDimitry Andric     OS.write(0);
4860b57cec5SDimitry Andric   uint64_t CSSummaryOffset = 0;
4870b57cec5SDimitry Andric   uint64_t CSSummarySize = 0;
48881ad6265SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::ContextSensitive)) {
4890b57cec5SDimitry Andric     CSSummaryOffset = OS.tell();
4900b57cec5SDimitry Andric     CSSummarySize = SummarySize / sizeof(uint64_t);
4910b57cec5SDimitry Andric     for (unsigned I = 0; I < CSSummarySize; I++)
4920b57cec5SDimitry Andric       OS.write(0);
4930b57cec5SDimitry Andric   }
4940b57cec5SDimitry Andric 
4950b57cec5SDimitry Andric   // Write the hash table.
4960b57cec5SDimitry Andric   uint64_t HashTableStart = Generator.Emit(OS.OS, *InfoObj);
4970b57cec5SDimitry Andric 
49881ad6265SDimitry Andric   // Write the MemProf profile data if we have it. This includes a simple schema
49981ad6265SDimitry Andric   // with the format described below followed by the hashtable:
50081ad6265SDimitry Andric   // uint64_t RecordTableOffset = RecordTableGenerator.Emit
50181ad6265SDimitry Andric   // uint64_t FramePayloadOffset = Stream offset before emitting the frame table
50281ad6265SDimitry Andric   // uint64_t FrameTableOffset = FrameTableGenerator.Emit
50381ad6265SDimitry Andric   // uint64_t Num schema entries
50481ad6265SDimitry Andric   // uint64_t Schema entry 0
50581ad6265SDimitry Andric   // uint64_t Schema entry 1
50681ad6265SDimitry Andric   // ....
50781ad6265SDimitry Andric   // uint64_t Schema entry N - 1
50881ad6265SDimitry Andric   // OnDiskChainedHashTable MemProfRecordData
50981ad6265SDimitry Andric   // OnDiskChainedHashTable MemProfFrameData
51081ad6265SDimitry Andric   uint64_t MemProfSectionStart = 0;
51181ad6265SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::MemProf)) {
51281ad6265SDimitry Andric     MemProfSectionStart = OS.tell();
51381ad6265SDimitry Andric     OS.write(0ULL); // Reserve space for the memprof record table offset.
51481ad6265SDimitry Andric     OS.write(0ULL); // Reserve space for the memprof frame payload offset.
51581ad6265SDimitry Andric     OS.write(0ULL); // Reserve space for the memprof frame table offset.
51681ad6265SDimitry Andric 
51781ad6265SDimitry Andric     auto Schema = memprof::PortableMemInfoBlock::getSchema();
51881ad6265SDimitry Andric     OS.write(static_cast<uint64_t>(Schema.size()));
51981ad6265SDimitry Andric     for (const auto Id : Schema) {
52081ad6265SDimitry Andric       OS.write(static_cast<uint64_t>(Id));
52181ad6265SDimitry Andric     }
52281ad6265SDimitry Andric 
52381ad6265SDimitry Andric     auto RecordWriter = std::make_unique<memprof::RecordWriterTrait>();
52481ad6265SDimitry Andric     RecordWriter->Schema = &Schema;
52581ad6265SDimitry Andric     OnDiskChainedHashTableGenerator<memprof::RecordWriterTrait>
52681ad6265SDimitry Andric         RecordTableGenerator;
52781ad6265SDimitry Andric     for (auto &I : MemProfRecordData) {
52881ad6265SDimitry Andric       // Insert the key (func hash) and value (memprof record).
52981ad6265SDimitry Andric       RecordTableGenerator.insert(I.first, I.second);
53081ad6265SDimitry Andric     }
53181ad6265SDimitry Andric 
53281ad6265SDimitry Andric     uint64_t RecordTableOffset =
53381ad6265SDimitry Andric         RecordTableGenerator.Emit(OS.OS, *RecordWriter);
53481ad6265SDimitry Andric 
53581ad6265SDimitry Andric     uint64_t FramePayloadOffset = OS.tell();
53681ad6265SDimitry Andric 
53781ad6265SDimitry Andric     auto FrameWriter = std::make_unique<memprof::FrameWriterTrait>();
53881ad6265SDimitry Andric     OnDiskChainedHashTableGenerator<memprof::FrameWriterTrait>
53981ad6265SDimitry Andric         FrameTableGenerator;
54081ad6265SDimitry Andric     for (auto &I : MemProfFrameData) {
54181ad6265SDimitry Andric       // Insert the key (frame id) and value (frame contents).
54281ad6265SDimitry Andric       FrameTableGenerator.insert(I.first, I.second);
54381ad6265SDimitry Andric     }
54481ad6265SDimitry Andric 
54581ad6265SDimitry Andric     uint64_t FrameTableOffset = FrameTableGenerator.Emit(OS.OS, *FrameWriter);
54681ad6265SDimitry Andric 
54781ad6265SDimitry Andric     PatchItem PatchItems[] = {
54881ad6265SDimitry Andric         {MemProfSectionStart, &RecordTableOffset, 1},
54981ad6265SDimitry Andric         {MemProfSectionStart + sizeof(uint64_t), &FramePayloadOffset, 1},
55081ad6265SDimitry Andric         {MemProfSectionStart + 2 * sizeof(uint64_t), &FrameTableOffset, 1},
55181ad6265SDimitry Andric     };
55281ad6265SDimitry Andric     OS.patch(PatchItems, 3);
55381ad6265SDimitry Andric   }
55481ad6265SDimitry Andric 
555bdd1243dSDimitry Andric   // BinaryIdSection has two parts:
556bdd1243dSDimitry Andric   // 1. uint64_t BinaryIdsSectionSize
557bdd1243dSDimitry Andric   // 2. list of binary ids that consist of:
558bdd1243dSDimitry Andric   //    a. uint64_t BinaryIdLength
559bdd1243dSDimitry Andric   //    b. uint8_t  BinaryIdData
560bdd1243dSDimitry Andric   //    c. uint8_t  Padding (if necessary)
561bdd1243dSDimitry Andric   uint64_t BinaryIdSectionStart = OS.tell();
562bdd1243dSDimitry Andric   // Calculate size of binary section.
563bdd1243dSDimitry Andric   uint64_t BinaryIdsSectionSize = 0;
564bdd1243dSDimitry Andric 
565bdd1243dSDimitry Andric   // Remove duplicate binary ids.
566bdd1243dSDimitry Andric   llvm::sort(BinaryIds);
567bdd1243dSDimitry Andric   BinaryIds.erase(std::unique(BinaryIds.begin(), BinaryIds.end()),
568bdd1243dSDimitry Andric                   BinaryIds.end());
569bdd1243dSDimitry Andric 
570bdd1243dSDimitry Andric   for (auto BI : BinaryIds) {
571bdd1243dSDimitry Andric     // Increment by binary id length data type size.
572bdd1243dSDimitry Andric     BinaryIdsSectionSize += sizeof(uint64_t);
573bdd1243dSDimitry Andric     // Increment by binary id data length, aligned to 8 bytes.
574bdd1243dSDimitry Andric     BinaryIdsSectionSize += alignToPowerOf2(BI.size(), sizeof(uint64_t));
575bdd1243dSDimitry Andric   }
576bdd1243dSDimitry Andric   // Write binary ids section size.
577bdd1243dSDimitry Andric   OS.write(BinaryIdsSectionSize);
578bdd1243dSDimitry Andric 
579bdd1243dSDimitry Andric   for (auto BI : BinaryIds) {
580bdd1243dSDimitry Andric     uint64_t BILen = BI.size();
581bdd1243dSDimitry Andric     // Write binary id length.
582bdd1243dSDimitry Andric     OS.write(BILen);
583bdd1243dSDimitry Andric     // Write binary id data.
584bdd1243dSDimitry Andric     for (unsigned K = 0; K < BILen; K++)
585bdd1243dSDimitry Andric       OS.writeByte(BI[K]);
586bdd1243dSDimitry Andric     // Write padding if necessary.
587bdd1243dSDimitry Andric     uint64_t PaddingSize = alignToPowerOf2(BILen, sizeof(uint64_t)) - BILen;
588bdd1243dSDimitry Andric     for (unsigned K = 0; K < PaddingSize; K++)
589bdd1243dSDimitry Andric       OS.writeByte(0);
590bdd1243dSDimitry Andric   }
591bdd1243dSDimitry Andric 
592*fe013be4SDimitry Andric   uint64_t TemporalProfTracesSectionStart = 0;
593*fe013be4SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::TemporalProfile)) {
594*fe013be4SDimitry Andric     TemporalProfTracesSectionStart = OS.tell();
595*fe013be4SDimitry Andric     OS.write(TemporalProfTraces.size());
596*fe013be4SDimitry Andric     OS.write(TemporalProfTraceStreamSize);
597*fe013be4SDimitry Andric     for (auto &Trace : TemporalProfTraces) {
598*fe013be4SDimitry Andric       OS.write(Trace.Weight);
599*fe013be4SDimitry Andric       OS.write(Trace.FunctionNameRefs.size());
600*fe013be4SDimitry Andric       for (auto &NameRef : Trace.FunctionNameRefs)
601*fe013be4SDimitry Andric         OS.write(NameRef);
602*fe013be4SDimitry Andric     }
603*fe013be4SDimitry Andric   }
604*fe013be4SDimitry Andric 
6050b57cec5SDimitry Andric   // Allocate space for data to be serialized out.
6060b57cec5SDimitry Andric   std::unique_ptr<IndexedInstrProf::Summary> TheSummary =
6070b57cec5SDimitry Andric       IndexedInstrProf::allocSummary(SummarySize);
6080b57cec5SDimitry Andric   // Compute the Summary and copy the data to the data
6090b57cec5SDimitry Andric   // structure to be serialized out (to disk or buffer).
6100b57cec5SDimitry Andric   std::unique_ptr<ProfileSummary> PS = ISB.getSummary();
6110b57cec5SDimitry Andric   setSummary(TheSummary.get(), *PS);
6120b57cec5SDimitry Andric   InfoObj->SummaryBuilder = nullptr;
6130b57cec5SDimitry Andric 
6140b57cec5SDimitry Andric   // For Context Sensitive summary.
6150b57cec5SDimitry Andric   std::unique_ptr<IndexedInstrProf::Summary> TheCSSummary = nullptr;
61681ad6265SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::ContextSensitive)) {
6170b57cec5SDimitry Andric     TheCSSummary = IndexedInstrProf::allocSummary(SummarySize);
6180b57cec5SDimitry Andric     std::unique_ptr<ProfileSummary> CSPS = CSISB.getSummary();
6190b57cec5SDimitry Andric     setSummary(TheCSSummary.get(), *CSPS);
6200b57cec5SDimitry Andric   }
6210b57cec5SDimitry Andric   InfoObj->CSSummaryBuilder = nullptr;
6220b57cec5SDimitry Andric 
6230b57cec5SDimitry Andric   // Now do the final patch:
6240b57cec5SDimitry Andric   PatchItem PatchItems[] = {
6250b57cec5SDimitry Andric       // Patch the Header.HashOffset field.
6260b57cec5SDimitry Andric       {HashTableStartFieldOffset, &HashTableStart, 1},
627bdd1243dSDimitry Andric       // Patch the Header.MemProfOffset (=0 for profiles without MemProf
628bdd1243dSDimitry Andric       // data).
62981ad6265SDimitry Andric       {MemProfSectionOffset, &MemProfSectionStart, 1},
630bdd1243dSDimitry Andric       // Patch the Header.BinaryIdSectionOffset.
631bdd1243dSDimitry Andric       {BinaryIdSectionOffset, &BinaryIdSectionStart, 1},
632*fe013be4SDimitry Andric       // Patch the Header.TemporalProfTracesOffset (=0 for profiles without
633*fe013be4SDimitry Andric       // traces).
634*fe013be4SDimitry Andric       {TemporalProfTracesOffset, &TemporalProfTracesSectionStart, 1},
6350b57cec5SDimitry Andric       // Patch the summary data.
6360b57cec5SDimitry Andric       {SummaryOffset, reinterpret_cast<uint64_t *>(TheSummary.get()),
6370b57cec5SDimitry Andric        (int)(SummarySize / sizeof(uint64_t))},
6380b57cec5SDimitry Andric       {CSSummaryOffset, reinterpret_cast<uint64_t *>(TheCSSummary.get()),
6390b57cec5SDimitry Andric        (int)CSSummarySize}};
6400b57cec5SDimitry Andric 
641bdd1243dSDimitry Andric   OS.patch(PatchItems, std::size(PatchItems));
642fe6060f1SDimitry Andric 
643fe6060f1SDimitry Andric   for (const auto &I : FunctionData)
644fe6060f1SDimitry Andric     for (const auto &F : I.getValue())
645fe6060f1SDimitry Andric       if (Error E = validateRecord(F.second))
646fe6060f1SDimitry Andric         return E;
647fe6060f1SDimitry Andric 
648fe6060f1SDimitry Andric   return Error::success();
6490b57cec5SDimitry Andric }
6500b57cec5SDimitry Andric 
651fe6060f1SDimitry Andric Error InstrProfWriter::write(raw_fd_ostream &OS) {
6520b57cec5SDimitry Andric   // Write the hash table.
6530b57cec5SDimitry Andric   ProfOStream POS(OS);
654fe6060f1SDimitry Andric   return writeImpl(POS);
6550b57cec5SDimitry Andric }
6560b57cec5SDimitry Andric 
657*fe013be4SDimitry Andric Error InstrProfWriter::write(raw_string_ostream &OS) {
658*fe013be4SDimitry Andric   ProfOStream POS(OS);
659*fe013be4SDimitry Andric   return writeImpl(POS);
660*fe013be4SDimitry Andric }
661*fe013be4SDimitry Andric 
6620b57cec5SDimitry Andric std::unique_ptr<MemoryBuffer> InstrProfWriter::writeBuffer() {
6630b57cec5SDimitry Andric   std::string Data;
6640b57cec5SDimitry Andric   raw_string_ostream OS(Data);
6650b57cec5SDimitry Andric   // Write the hash table.
666*fe013be4SDimitry Andric   if (Error E = write(OS))
667fe6060f1SDimitry Andric     return nullptr;
6680b57cec5SDimitry Andric   // Return this in an aligned memory buffer.
6690b57cec5SDimitry Andric   return MemoryBuffer::getMemBufferCopy(Data);
6700b57cec5SDimitry Andric }
6710b57cec5SDimitry Andric 
6720b57cec5SDimitry Andric static const char *ValueProfKindStr[] = {
6730b57cec5SDimitry Andric #define VALUE_PROF_KIND(Enumerator, Value, Descr) #Enumerator,
6740b57cec5SDimitry Andric #include "llvm/ProfileData/InstrProfData.inc"
6750b57cec5SDimitry Andric };
6760b57cec5SDimitry Andric 
677fe6060f1SDimitry Andric Error InstrProfWriter::validateRecord(const InstrProfRecord &Func) {
678fe6060f1SDimitry Andric   for (uint32_t VK = 0; VK <= IPVK_Last; VK++) {
679fe6060f1SDimitry Andric     uint32_t NS = Func.getNumValueSites(VK);
680fe6060f1SDimitry Andric     if (!NS)
681fe6060f1SDimitry Andric       continue;
682fe6060f1SDimitry Andric     for (uint32_t S = 0; S < NS; S++) {
683fe6060f1SDimitry Andric       uint32_t ND = Func.getNumValueDataForSite(VK, S);
684fe6060f1SDimitry Andric       std::unique_ptr<InstrProfValueData[]> VD = Func.getValueForSite(VK, S);
685bdd1243dSDimitry Andric       DenseSet<uint64_t> SeenValues;
686fe6060f1SDimitry Andric       for (uint32_t I = 0; I < ND; I++)
687bdd1243dSDimitry Andric         if ((VK != IPVK_IndirectCallTarget) && !SeenValues.insert(VD[I].Value).second)
688fe6060f1SDimitry Andric           return make_error<InstrProfError>(instrprof_error::invalid_prof);
689fe6060f1SDimitry Andric     }
690fe6060f1SDimitry Andric   }
691fe6060f1SDimitry Andric 
692fe6060f1SDimitry Andric   return Error::success();
693fe6060f1SDimitry Andric }
694fe6060f1SDimitry Andric 
6950b57cec5SDimitry Andric void InstrProfWriter::writeRecordInText(StringRef Name, uint64_t Hash,
6960b57cec5SDimitry Andric                                         const InstrProfRecord &Func,
6970b57cec5SDimitry Andric                                         InstrProfSymtab &Symtab,
6980b57cec5SDimitry Andric                                         raw_fd_ostream &OS) {
6990b57cec5SDimitry Andric   OS << Name << "\n";
7000b57cec5SDimitry Andric   OS << "# Func Hash:\n" << Hash << "\n";
7010b57cec5SDimitry Andric   OS << "# Num Counters:\n" << Func.Counts.size() << "\n";
7020b57cec5SDimitry Andric   OS << "# Counter Values:\n";
7030b57cec5SDimitry Andric   for (uint64_t Count : Func.Counts)
7040b57cec5SDimitry Andric     OS << Count << "\n";
7050b57cec5SDimitry Andric 
7060b57cec5SDimitry Andric   uint32_t NumValueKinds = Func.getNumValueKinds();
7070b57cec5SDimitry Andric   if (!NumValueKinds) {
7080b57cec5SDimitry Andric     OS << "\n";
7090b57cec5SDimitry Andric     return;
7100b57cec5SDimitry Andric   }
7110b57cec5SDimitry Andric 
7120b57cec5SDimitry Andric   OS << "# Num Value Kinds:\n" << Func.getNumValueKinds() << "\n";
7130b57cec5SDimitry Andric   for (uint32_t VK = 0; VK < IPVK_Last + 1; VK++) {
7140b57cec5SDimitry Andric     uint32_t NS = Func.getNumValueSites(VK);
7150b57cec5SDimitry Andric     if (!NS)
7160b57cec5SDimitry Andric       continue;
7170b57cec5SDimitry Andric     OS << "# ValueKind = " << ValueProfKindStr[VK] << ":\n" << VK << "\n";
7180b57cec5SDimitry Andric     OS << "# NumValueSites:\n" << NS << "\n";
7190b57cec5SDimitry Andric     for (uint32_t S = 0; S < NS; S++) {
7200b57cec5SDimitry Andric       uint32_t ND = Func.getNumValueDataForSite(VK, S);
7210b57cec5SDimitry Andric       OS << ND << "\n";
7220b57cec5SDimitry Andric       std::unique_ptr<InstrProfValueData[]> VD = Func.getValueForSite(VK, S);
7230b57cec5SDimitry Andric       for (uint32_t I = 0; I < ND; I++) {
7240b57cec5SDimitry Andric         if (VK == IPVK_IndirectCallTarget)
7250b57cec5SDimitry Andric           OS << Symtab.getFuncNameOrExternalSymbol(VD[I].Value) << ":"
7260b57cec5SDimitry Andric              << VD[I].Count << "\n";
7270b57cec5SDimitry Andric         else
7280b57cec5SDimitry Andric           OS << VD[I].Value << ":" << VD[I].Count << "\n";
7290b57cec5SDimitry Andric       }
7300b57cec5SDimitry Andric     }
7310b57cec5SDimitry Andric   }
7320b57cec5SDimitry Andric 
7330b57cec5SDimitry Andric   OS << "\n";
7340b57cec5SDimitry Andric }
7350b57cec5SDimitry Andric 
7360b57cec5SDimitry Andric Error InstrProfWriter::writeText(raw_fd_ostream &OS) {
7371fd87a68SDimitry Andric   // Check CS first since it implies an IR level profile.
73881ad6265SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::ContextSensitive))
7390b57cec5SDimitry Andric     OS << "# CSIR level Instrumentation Flag\n:csir\n";
74081ad6265SDimitry Andric   else if (static_cast<bool>(ProfileKind & InstrProfKind::IRInstrumentation))
7411fd87a68SDimitry Andric     OS << "# IR level Instrumentation Flag\n:ir\n";
7421fd87a68SDimitry Andric 
74381ad6265SDimitry Andric   if (static_cast<bool>(ProfileKind &
74481ad6265SDimitry Andric                         InstrProfKind::FunctionEntryInstrumentation))
745e8d8bef9SDimitry Andric     OS << "# Always instrument the function entry block\n:entry_first\n";
7460b57cec5SDimitry Andric   InstrProfSymtab Symtab;
7470b57cec5SDimitry Andric 
7480b57cec5SDimitry Andric   using FuncPair = detail::DenseMapPair<uint64_t, InstrProfRecord>;
7490b57cec5SDimitry Andric   using RecordType = std::pair<StringRef, FuncPair>;
7500b57cec5SDimitry Andric   SmallVector<RecordType, 4> OrderedFuncData;
7510b57cec5SDimitry Andric 
7520b57cec5SDimitry Andric   for (const auto &I : FunctionData) {
7530b57cec5SDimitry Andric     if (shouldEncodeData(I.getValue())) {
7540b57cec5SDimitry Andric       if (Error E = Symtab.addFuncName(I.getKey()))
7550b57cec5SDimitry Andric         return E;
7560b57cec5SDimitry Andric       for (const auto &Func : I.getValue())
7570b57cec5SDimitry Andric         OrderedFuncData.push_back(std::make_pair(I.getKey(), Func));
7580b57cec5SDimitry Andric     }
7590b57cec5SDimitry Andric   }
7600b57cec5SDimitry Andric 
761*fe013be4SDimitry Andric   if (static_cast<bool>(ProfileKind & InstrProfKind::TemporalProfile))
762*fe013be4SDimitry Andric     writeTextTemporalProfTraceData(OS, Symtab);
763*fe013be4SDimitry Andric 
7640b57cec5SDimitry Andric   llvm::sort(OrderedFuncData, [](const RecordType &A, const RecordType &B) {
7650b57cec5SDimitry Andric     return std::tie(A.first, A.second.first) <
7660b57cec5SDimitry Andric            std::tie(B.first, B.second.first);
7670b57cec5SDimitry Andric   });
7680b57cec5SDimitry Andric 
7690b57cec5SDimitry Andric   for (const auto &record : OrderedFuncData) {
7700b57cec5SDimitry Andric     const StringRef &Name = record.first;
7710b57cec5SDimitry Andric     const FuncPair &Func = record.second;
7720b57cec5SDimitry Andric     writeRecordInText(Name, Func.first, Func.second, Symtab, OS);
7730b57cec5SDimitry Andric   }
7740b57cec5SDimitry Andric 
775fe6060f1SDimitry Andric   for (const auto &record : OrderedFuncData) {
776fe6060f1SDimitry Andric     const FuncPair &Func = record.second;
777fe6060f1SDimitry Andric     if (Error E = validateRecord(Func.second))
778fe6060f1SDimitry Andric       return E;
779fe6060f1SDimitry Andric   }
780fe6060f1SDimitry Andric 
7810b57cec5SDimitry Andric   return Error::success();
7820b57cec5SDimitry Andric }
783*fe013be4SDimitry Andric 
784*fe013be4SDimitry Andric void InstrProfWriter::writeTextTemporalProfTraceData(raw_fd_ostream &OS,
785*fe013be4SDimitry Andric                                                      InstrProfSymtab &Symtab) {
786*fe013be4SDimitry Andric   OS << ":temporal_prof_traces\n";
787*fe013be4SDimitry Andric   OS << "# Num Temporal Profile Traces:\n" << TemporalProfTraces.size() << "\n";
788*fe013be4SDimitry Andric   OS << "# Temporal Profile Trace Stream Size:\n"
789*fe013be4SDimitry Andric      << TemporalProfTraceStreamSize << "\n";
790*fe013be4SDimitry Andric   for (auto &Trace : TemporalProfTraces) {
791*fe013be4SDimitry Andric     OS << "# Weight:\n" << Trace.Weight << "\n";
792*fe013be4SDimitry Andric     for (auto &NameRef : Trace.FunctionNameRefs)
793*fe013be4SDimitry Andric       OS << Symtab.getFuncName(NameRef) << ",";
794*fe013be4SDimitry Andric     OS << "\n";
795*fe013be4SDimitry Andric   }
796*fe013be4SDimitry Andric   OS << "\n";
797*fe013be4SDimitry Andric }
798