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