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