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