1 //===- InstrProfWriter.cpp - Instrumented profiling writer ----------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file contains support for writing profiling data for clang's
10 // instrumentation based PGO and coverage.
12 //===----------------------------------------------------------------------===//
14 #include "llvm/ProfileData/InstrProfWriter.h"
15 #include "llvm/ADT/STLExtras.h"
16 #include "llvm/ADT/SetVector.h"
17 #include "llvm/ADT/StringRef.h"
18 #include "llvm/IR/ProfileSummary.h"
19 #include "llvm/ProfileData/InstrProf.h"
20 #include "llvm/ProfileData/MemProf.h"
21 #include "llvm/ProfileData/ProfileCommon.h"
22 #include "llvm/Support/Endian.h"
23 #include "llvm/Support/EndianStream.h"
24 #include "llvm/Support/Error.h"
25 #include "llvm/Support/MemoryBuffer.h"
26 #include "llvm/Support/OnDiskHashTable.h"
27 #include "llvm/Support/raw_ostream.h"
37 // A struct to define how the data stream should be patched. For Indexed
38 // profiling, only uint64_t data type is needed.
40 uint64_t Pos
; // Where to patch.
41 uint64_t *D
; // Pointer to an array of source data.
42 int N
; // Number of elements in \c D array.
47 // A wrapper class to abstract writer stream with support of bytes
51 ProfOStream(raw_fd_ostream
&FD
)
52 : IsFDOStream(true), OS(FD
), LE(FD
, llvm::endianness::little
) {}
53 ProfOStream(raw_string_ostream
&STR
)
54 : IsFDOStream(false), OS(STR
), LE(STR
, llvm::endianness::little
) {}
56 uint64_t tell() { return OS
.tell(); }
57 void write(uint64_t V
) { LE
.write
<uint64_t>(V
); }
58 void writeByte(uint8_t V
) { LE
.write
<uint8_t>(V
); }
60 // \c patch can only be called when all data is written and flushed.
61 // For raw_string_ostream, the patch is done on the target string
62 // directly and it won't be reflected in the stream's internal buffer.
63 void patch(PatchItem
*P
, int NItems
) {
64 using namespace support
;
67 raw_fd_ostream
&FDOStream
= static_cast<raw_fd_ostream
&>(OS
);
68 const uint64_t LastPos
= FDOStream
.tell();
69 for (int K
= 0; K
< NItems
; K
++) {
70 FDOStream
.seek(P
[K
].Pos
);
71 for (int I
= 0; I
< P
[K
].N
; I
++)
74 // Reset the stream to the last position after patching so that users
75 // don't accidentally overwrite data. This makes it consistent with
76 // the string stream below which replaces the data directly.
77 FDOStream
.seek(LastPos
);
79 raw_string_ostream
&SOStream
= static_cast<raw_string_ostream
&>(OS
);
80 std::string
&Data
= SOStream
.str(); // with flush
81 for (int K
= 0; K
< NItems
; K
++) {
82 for (int I
= 0; I
< P
[K
].N
; I
++) {
84 endian::byte_swap
<uint64_t, llvm::endianness::little
>(P
[K
].D
[I
]);
85 Data
.replace(P
[K
].Pos
+ I
* sizeof(uint64_t), sizeof(uint64_t),
86 (const char *)&Bytes
, sizeof(uint64_t));
92 // If \c OS is an instance of \c raw_fd_ostream, this field will be
93 // true. Otherwise, \c OS will be an raw_string_ostream.
96 support::endian::Writer LE
;
99 class InstrProfRecordWriterTrait
{
101 using key_type
= StringRef
;
102 using key_type_ref
= StringRef
;
104 using data_type
= const InstrProfWriter::ProfilingData
*const;
105 using data_type_ref
= const InstrProfWriter::ProfilingData
*const;
107 using hash_value_type
= uint64_t;
108 using offset_type
= uint64_t;
110 llvm::endianness ValueProfDataEndianness
= llvm::endianness::little
;
111 InstrProfSummaryBuilder
*SummaryBuilder
;
112 InstrProfSummaryBuilder
*CSSummaryBuilder
;
114 InstrProfRecordWriterTrait() = default;
116 static hash_value_type
ComputeHash(key_type_ref K
) {
117 return IndexedInstrProf::ComputeHash(K
);
120 static std::pair
<offset_type
, offset_type
>
121 EmitKeyDataLength(raw_ostream
&Out
, key_type_ref K
, data_type_ref V
) {
122 using namespace support
;
124 endian::Writer
LE(Out
, llvm::endianness::little
);
126 offset_type N
= K
.size();
127 LE
.write
<offset_type
>(N
);
130 for (const auto &ProfileData
: *V
) {
131 const InstrProfRecord
&ProfRecord
= ProfileData
.second
;
132 M
+= sizeof(uint64_t); // The function hash
133 M
+= sizeof(uint64_t); // The size of the Counts vector
134 M
+= ProfRecord
.Counts
.size() * sizeof(uint64_t);
135 M
+= sizeof(uint64_t); // The size of the Bitmap vector
136 M
+= ProfRecord
.BitmapBytes
.size() * sizeof(uint64_t);
139 M
+= ValueProfData::getSize(ProfileData
.second
);
141 LE
.write
<offset_type
>(M
);
143 return std::make_pair(N
, M
);
146 void EmitKey(raw_ostream
&Out
, key_type_ref K
, offset_type N
) {
147 Out
.write(K
.data(), N
);
150 void EmitData(raw_ostream
&Out
, key_type_ref
, data_type_ref V
, offset_type
) {
151 using namespace support
;
153 endian::Writer
LE(Out
, llvm::endianness::little
);
154 for (const auto &ProfileData
: *V
) {
155 const InstrProfRecord
&ProfRecord
= ProfileData
.second
;
156 if (NamedInstrProfRecord::hasCSFlagInHash(ProfileData
.first
))
157 CSSummaryBuilder
->addRecord(ProfRecord
);
159 SummaryBuilder
->addRecord(ProfRecord
);
161 LE
.write
<uint64_t>(ProfileData
.first
); // Function hash
162 LE
.write
<uint64_t>(ProfRecord
.Counts
.size());
163 for (uint64_t I
: ProfRecord
.Counts
)
164 LE
.write
<uint64_t>(I
);
166 LE
.write
<uint64_t>(ProfRecord
.BitmapBytes
.size());
167 for (uint64_t I
: ProfRecord
.BitmapBytes
)
168 LE
.write
<uint64_t>(I
);
171 std::unique_ptr
<ValueProfData
> VDataPtr
=
172 ValueProfData::serializeFrom(ProfileData
.second
);
173 uint32_t S
= VDataPtr
->getSize();
174 VDataPtr
->swapBytesFromHost(ValueProfDataEndianness
);
175 Out
.write((const char *)VDataPtr
.get(), S
);
180 } // end namespace llvm
182 InstrProfWriter::InstrProfWriter(bool Sparse
,
183 uint64_t TemporalProfTraceReservoirSize
,
184 uint64_t MaxTemporalProfTraceLength
)
185 : Sparse(Sparse
), MaxTemporalProfTraceLength(MaxTemporalProfTraceLength
),
186 TemporalProfTraceReservoirSize(TemporalProfTraceReservoirSize
),
187 InfoObj(new InstrProfRecordWriterTrait()) {}
189 InstrProfWriter::~InstrProfWriter() { delete InfoObj
; }
191 // Internal interface for testing purpose only.
192 void InstrProfWriter::setValueProfDataEndianness(llvm::endianness Endianness
) {
193 InfoObj
->ValueProfDataEndianness
= Endianness
;
196 void InstrProfWriter::setOutputSparse(bool Sparse
) {
197 this->Sparse
= Sparse
;
200 void InstrProfWriter::addRecord(NamedInstrProfRecord
&&I
, uint64_t Weight
,
201 function_ref
<void(Error
)> Warn
) {
204 addRecord(Name
, Hash
, std::move(I
), Weight
, Warn
);
207 void InstrProfWriter::overlapRecord(NamedInstrProfRecord
&&Other
,
208 OverlapStats
&Overlap
,
209 OverlapStats
&FuncLevelOverlap
,
210 const OverlapFuncFilters
&FuncFilter
) {
211 auto Name
= Other
.Name
;
212 auto Hash
= Other
.Hash
;
213 Other
.accumulateCounts(FuncLevelOverlap
.Test
);
214 if (!FunctionData
.contains(Name
)) {
215 Overlap
.addOneUnique(FuncLevelOverlap
.Test
);
218 if (FuncLevelOverlap
.Test
.CountSum
< 1.0f
) {
219 Overlap
.Overlap
.NumEntries
+= 1;
222 auto &ProfileDataMap
= FunctionData
[Name
];
224 ProfilingData::iterator Where
;
225 std::tie(Where
, NewFunc
) =
226 ProfileDataMap
.insert(std::make_pair(Hash
, InstrProfRecord()));
228 Overlap
.addOneMismatch(FuncLevelOverlap
.Test
);
231 InstrProfRecord
&Dest
= Where
->second
;
233 uint64_t ValueCutoff
= FuncFilter
.ValueCutoff
;
234 if (!FuncFilter
.NameFilter
.empty() && Name
.contains(FuncFilter
.NameFilter
))
237 Dest
.overlap(Other
, Overlap
, FuncLevelOverlap
, ValueCutoff
);
240 void InstrProfWriter::addRecord(StringRef Name
, uint64_t Hash
,
241 InstrProfRecord
&&I
, uint64_t Weight
,
242 function_ref
<void(Error
)> Warn
) {
243 auto &ProfileDataMap
= FunctionData
[Name
];
246 ProfilingData::iterator Where
;
247 std::tie(Where
, NewFunc
) =
248 ProfileDataMap
.insert(std::make_pair(Hash
, InstrProfRecord()));
249 InstrProfRecord
&Dest
= Where
->second
;
251 auto MapWarn
= [&](instrprof_error E
) {
252 Warn(make_error
<InstrProfError
>(E
));
256 // We've never seen a function with this name and hash, add it.
259 Dest
.scale(Weight
, 1, MapWarn
);
261 // We're updating a function we've seen before.
262 Dest
.merge(I
, Weight
, MapWarn
);
265 Dest
.sortValueData();
268 void InstrProfWriter::addMemProfRecord(
269 const Function::GUID Id
, const memprof::IndexedMemProfRecord
&Record
) {
270 auto Result
= MemProfRecordData
.insert({Id
, Record
});
271 // If we inserted a new record then we are done.
275 memprof::IndexedMemProfRecord
&Existing
= Result
.first
->second
;
276 Existing
.merge(Record
);
279 bool InstrProfWriter::addMemProfFrame(const memprof::FrameId Id
,
280 const memprof::Frame
&Frame
,
281 function_ref
<void(Error
)> Warn
) {
282 auto Result
= MemProfFrameData
.insert({Id
, Frame
});
283 // If a mapping already exists for the current frame id and it does not
284 // match the new mapping provided then reset the existing contents and bail
285 // out. We don't support the merging of memprof data whose Frame -> Id
286 // mapping across profiles is inconsistent.
287 if (!Result
.second
&& Result
.first
->second
!= Frame
) {
288 Warn(make_error
<InstrProfError
>(instrprof_error::malformed
,
289 "frame to id mapping mismatch"));
295 void InstrProfWriter::addBinaryIds(ArrayRef
<llvm::object::BuildID
> BIs
) {
296 llvm::append_range(BinaryIds
, BIs
);
299 void InstrProfWriter::addTemporalProfileTrace(TemporalProfTraceTy Trace
) {
300 if (Trace
.FunctionNameRefs
.size() > MaxTemporalProfTraceLength
)
301 Trace
.FunctionNameRefs
.resize(MaxTemporalProfTraceLength
);
302 if (Trace
.FunctionNameRefs
.empty())
305 if (TemporalProfTraceStreamSize
< TemporalProfTraceReservoirSize
) {
306 // Simply append the trace if we have not yet hit our reservoir size limit.
307 TemporalProfTraces
.push_back(std::move(Trace
));
309 // Otherwise, replace a random trace in the stream.
310 std::uniform_int_distribution
<uint64_t> Distribution(
311 0, TemporalProfTraceStreamSize
);
312 uint64_t RandomIndex
= Distribution(RNG
);
313 if (RandomIndex
< TemporalProfTraces
.size())
314 TemporalProfTraces
[RandomIndex
] = std::move(Trace
);
316 ++TemporalProfTraceStreamSize
;
319 void InstrProfWriter::addTemporalProfileTraces(
320 SmallVectorImpl
<TemporalProfTraceTy
> &SrcTraces
, uint64_t SrcStreamSize
) {
321 // Assume that the source has the same reservoir size as the destination to
322 // avoid needing to record it in the indexed profile format.
324 (TemporalProfTraceStreamSize
> TemporalProfTraceReservoirSize
);
325 bool IsSrcSampled
= (SrcStreamSize
> TemporalProfTraceReservoirSize
);
326 if (!IsDestSampled
&& IsSrcSampled
) {
327 // If one of the traces are sampled, ensure that it belongs to Dest.
328 std::swap(TemporalProfTraces
, SrcTraces
);
329 std::swap(TemporalProfTraceStreamSize
, SrcStreamSize
);
330 std::swap(IsDestSampled
, IsSrcSampled
);
333 // If the source stream is not sampled, we add each source trace normally.
334 for (auto &Trace
: SrcTraces
)
335 addTemporalProfileTrace(std::move(Trace
));
338 // Otherwise, we find the traces that would have been removed if we added
339 // the whole source stream.
340 SmallSetVector
<uint64_t, 8> IndicesToReplace
;
341 for (uint64_t I
= 0; I
< SrcStreamSize
; I
++) {
342 std::uniform_int_distribution
<uint64_t> Distribution(
343 0, TemporalProfTraceStreamSize
);
344 uint64_t RandomIndex
= Distribution(RNG
);
345 if (RandomIndex
< TemporalProfTraces
.size())
346 IndicesToReplace
.insert(RandomIndex
);
347 ++TemporalProfTraceStreamSize
;
349 // Then we insert a random sample of the source traces.
350 llvm::shuffle(SrcTraces
.begin(), SrcTraces
.end(), RNG
);
351 for (const auto &[Index
, Trace
] : llvm::zip(IndicesToReplace
, SrcTraces
))
352 TemporalProfTraces
[Index
] = std::move(Trace
);
355 void InstrProfWriter::mergeRecordsFromWriter(InstrProfWriter
&&IPW
,
356 function_ref
<void(Error
)> Warn
) {
357 for (auto &I
: IPW
.FunctionData
)
358 for (auto &Func
: I
.getValue())
359 addRecord(I
.getKey(), Func
.first
, std::move(Func
.second
), 1, Warn
);
361 BinaryIds
.reserve(BinaryIds
.size() + IPW
.BinaryIds
.size());
362 for (auto &I
: IPW
.BinaryIds
)
365 addTemporalProfileTraces(IPW
.TemporalProfTraces
,
366 IPW
.TemporalProfTraceStreamSize
);
368 MemProfFrameData
.reserve(IPW
.MemProfFrameData
.size());
369 for (auto &I
: IPW
.MemProfFrameData
) {
370 // If we weren't able to add the frame mappings then it doesn't make sense
371 // to try to merge the records from this profile.
372 if (!addMemProfFrame(I
.first
, I
.second
, Warn
))
376 MemProfRecordData
.reserve(IPW
.MemProfRecordData
.size());
377 for (auto &I
: IPW
.MemProfRecordData
) {
378 addMemProfRecord(I
.first
, I
.second
);
382 bool InstrProfWriter::shouldEncodeData(const ProfilingData
&PD
) {
385 for (const auto &Func
: PD
) {
386 const InstrProfRecord
&IPR
= Func
.second
;
387 if (llvm::any_of(IPR
.Counts
, [](uint64_t Count
) { return Count
> 0; }))
389 if (llvm::any_of(IPR
.BitmapBytes
, [](uint8_t Byte
) { return Byte
> 0; }))
395 static void setSummary(IndexedInstrProf::Summary
*TheSummary
,
396 ProfileSummary
&PS
) {
397 using namespace IndexedInstrProf
;
399 const std::vector
<ProfileSummaryEntry
> &Res
= PS
.getDetailedSummary();
400 TheSummary
->NumSummaryFields
= Summary::NumKinds
;
401 TheSummary
->NumCutoffEntries
= Res
.size();
402 TheSummary
->set(Summary::MaxFunctionCount
, PS
.getMaxFunctionCount());
403 TheSummary
->set(Summary::MaxBlockCount
, PS
.getMaxCount());
404 TheSummary
->set(Summary::MaxInternalBlockCount
, PS
.getMaxInternalCount());
405 TheSummary
->set(Summary::TotalBlockCount
, PS
.getTotalCount());
406 TheSummary
->set(Summary::TotalNumBlocks
, PS
.getNumCounts());
407 TheSummary
->set(Summary::TotalNumFunctions
, PS
.getNumFunctions());
408 for (unsigned I
= 0; I
< Res
.size(); I
++)
409 TheSummary
->setEntry(I
, Res
[I
]);
412 Error
InstrProfWriter::writeImpl(ProfOStream
&OS
) {
413 using namespace IndexedInstrProf
;
414 using namespace support
;
416 OnDiskChainedHashTableGenerator
<InstrProfRecordWriterTrait
> Generator
;
418 InstrProfSummaryBuilder
ISB(ProfileSummaryBuilder::DefaultCutoffs
);
419 InfoObj
->SummaryBuilder
= &ISB
;
420 InstrProfSummaryBuilder
CSISB(ProfileSummaryBuilder::DefaultCutoffs
);
421 InfoObj
->CSSummaryBuilder
= &CSISB
;
423 // Populate the hash table generator.
424 SmallVector
<std::pair
<StringRef
, const ProfilingData
*>, 0> OrderedData
;
425 for (const auto &I
: FunctionData
)
426 if (shouldEncodeData(I
.getValue()))
427 OrderedData
.emplace_back((I
.getKey()), &I
.getValue());
428 llvm::sort(OrderedData
, less_first());
429 for (const auto &I
: OrderedData
)
430 Generator
.insert(I
.first
, I
.second
);
433 IndexedInstrProf::Header Header
;
434 Header
.Magic
= IndexedInstrProf::Magic
;
435 Header
.Version
= IndexedInstrProf::ProfVersion::CurrentVersion
;
436 if (static_cast<bool>(ProfileKind
& InstrProfKind::IRInstrumentation
))
437 Header
.Version
|= VARIANT_MASK_IR_PROF
;
438 if (static_cast<bool>(ProfileKind
& InstrProfKind::ContextSensitive
))
439 Header
.Version
|= VARIANT_MASK_CSIR_PROF
;
440 if (static_cast<bool>(ProfileKind
&
441 InstrProfKind::FunctionEntryInstrumentation
))
442 Header
.Version
|= VARIANT_MASK_INSTR_ENTRY
;
443 if (static_cast<bool>(ProfileKind
& InstrProfKind::SingleByteCoverage
))
444 Header
.Version
|= VARIANT_MASK_BYTE_COVERAGE
;
445 if (static_cast<bool>(ProfileKind
& InstrProfKind::FunctionEntryOnly
))
446 Header
.Version
|= VARIANT_MASK_FUNCTION_ENTRY_ONLY
;
447 if (static_cast<bool>(ProfileKind
& InstrProfKind::MemProf
))
448 Header
.Version
|= VARIANT_MASK_MEMPROF
;
449 if (static_cast<bool>(ProfileKind
& InstrProfKind::TemporalProfile
))
450 Header
.Version
|= VARIANT_MASK_TEMPORAL_PROF
;
453 Header
.HashType
= static_cast<uint64_t>(IndexedInstrProf::HashType
);
454 Header
.HashOffset
= 0;
455 Header
.MemProfOffset
= 0;
456 Header
.BinaryIdOffset
= 0;
457 Header
.TemporalProfTracesOffset
= 0;
458 int N
= sizeof(IndexedInstrProf::Header
) / sizeof(uint64_t);
460 // Only write out all the fields except 'HashOffset', 'MemProfOffset',
461 // 'BinaryIdOffset' and `TemporalProfTracesOffset`. We need to remember the
462 // offset of these fields to allow back patching later.
463 for (int I
= 0; I
< N
- 4; I
++)
464 OS
.write(reinterpret_cast<uint64_t *>(&Header
)[I
]);
466 // Save the location of Header.HashOffset field in \c OS.
467 uint64_t HashTableStartFieldOffset
= OS
.tell();
468 // Reserve the space for HashOffset field.
471 // Save the location of MemProf profile data. This is stored in two parts as
472 // the schema and as a separate on-disk chained hashtable.
473 uint64_t MemProfSectionOffset
= OS
.tell();
474 // Reserve space for the MemProf table field to be patched later if this
475 // profile contains memory profile information.
478 // Save the location of binary ids section.
479 uint64_t BinaryIdSectionOffset
= OS
.tell();
480 // Reserve space for the BinaryIdOffset field to be patched later if this
481 // profile contains binary ids.
484 uint64_t TemporalProfTracesOffset
= OS
.tell();
487 // Reserve space to write profile summary data.
488 uint32_t NumEntries
= ProfileSummaryBuilder::DefaultCutoffs
.size();
489 uint32_t SummarySize
= Summary::getSize(Summary::NumKinds
, NumEntries
);
490 // Remember the summary offset.
491 uint64_t SummaryOffset
= OS
.tell();
492 for (unsigned I
= 0; I
< SummarySize
/ sizeof(uint64_t); I
++)
494 uint64_t CSSummaryOffset
= 0;
495 uint64_t CSSummarySize
= 0;
496 if (static_cast<bool>(ProfileKind
& InstrProfKind::ContextSensitive
)) {
497 CSSummaryOffset
= OS
.tell();
498 CSSummarySize
= SummarySize
/ sizeof(uint64_t);
499 for (unsigned I
= 0; I
< CSSummarySize
; I
++)
503 // Write the hash table.
504 uint64_t HashTableStart
= Generator
.Emit(OS
.OS
, *InfoObj
);
506 // Write the MemProf profile data if we have it. This includes a simple schema
507 // with the format described below followed by the hashtable:
508 // uint64_t RecordTableOffset = RecordTableGenerator.Emit
509 // uint64_t FramePayloadOffset = Stream offset before emitting the frame table
510 // uint64_t FrameTableOffset = FrameTableGenerator.Emit
511 // uint64_t Num schema entries
512 // uint64_t Schema entry 0
513 // uint64_t Schema entry 1
515 // uint64_t Schema entry N - 1
516 // OnDiskChainedHashTable MemProfRecordData
517 // OnDiskChainedHashTable MemProfFrameData
518 uint64_t MemProfSectionStart
= 0;
519 if (static_cast<bool>(ProfileKind
& InstrProfKind::MemProf
)) {
520 MemProfSectionStart
= OS
.tell();
521 OS
.write(0ULL); // Reserve space for the memprof record table offset.
522 OS
.write(0ULL); // Reserve space for the memprof frame payload offset.
523 OS
.write(0ULL); // Reserve space for the memprof frame table offset.
525 auto Schema
= memprof::PortableMemInfoBlock::getSchema();
526 OS
.write(static_cast<uint64_t>(Schema
.size()));
527 for (const auto Id
: Schema
) {
528 OS
.write(static_cast<uint64_t>(Id
));
531 auto RecordWriter
= std::make_unique
<memprof::RecordWriterTrait
>();
532 RecordWriter
->Schema
= &Schema
;
533 OnDiskChainedHashTableGenerator
<memprof::RecordWriterTrait
>
534 RecordTableGenerator
;
535 for (auto &I
: MemProfRecordData
) {
536 // Insert the key (func hash) and value (memprof record).
537 RecordTableGenerator
.insert(I
.first
, I
.second
);
539 // Release the memory of this MapVector as it is no longer needed.
540 MemProfRecordData
.clear();
542 // The call to Emit invokes RecordWriterTrait::EmitData which destructs
543 // the memprof record copies owned by the RecordTableGenerator. This works
544 // because the RecordTableGenerator is not used after this point.
545 uint64_t RecordTableOffset
=
546 RecordTableGenerator
.Emit(OS
.OS
, *RecordWriter
);
548 uint64_t FramePayloadOffset
= OS
.tell();
550 auto FrameWriter
= std::make_unique
<memprof::FrameWriterTrait
>();
551 OnDiskChainedHashTableGenerator
<memprof::FrameWriterTrait
>
553 for (auto &I
: MemProfFrameData
) {
554 // Insert the key (frame id) and value (frame contents).
555 FrameTableGenerator
.insert(I
.first
, I
.second
);
557 // Release the memory of this MapVector as it is no longer needed.
558 MemProfFrameData
.clear();
560 uint64_t FrameTableOffset
= FrameTableGenerator
.Emit(OS
.OS
, *FrameWriter
);
562 PatchItem PatchItems
[] = {
563 {MemProfSectionStart
, &RecordTableOffset
, 1},
564 {MemProfSectionStart
+ sizeof(uint64_t), &FramePayloadOffset
, 1},
565 {MemProfSectionStart
+ 2 * sizeof(uint64_t), &FrameTableOffset
, 1},
567 OS
.patch(PatchItems
, 3);
570 // BinaryIdSection has two parts:
571 // 1. uint64_t BinaryIdsSectionSize
572 // 2. list of binary ids that consist of:
573 // a. uint64_t BinaryIdLength
574 // b. uint8_t BinaryIdData
575 // c. uint8_t Padding (if necessary)
576 uint64_t BinaryIdSectionStart
= OS
.tell();
577 // Calculate size of binary section.
578 uint64_t BinaryIdsSectionSize
= 0;
580 // Remove duplicate binary ids.
581 llvm::sort(BinaryIds
);
582 BinaryIds
.erase(std::unique(BinaryIds
.begin(), BinaryIds
.end()),
585 for (auto BI
: BinaryIds
) {
586 // Increment by binary id length data type size.
587 BinaryIdsSectionSize
+= sizeof(uint64_t);
588 // Increment by binary id data length, aligned to 8 bytes.
589 BinaryIdsSectionSize
+= alignToPowerOf2(BI
.size(), sizeof(uint64_t));
591 // Write binary ids section size.
592 OS
.write(BinaryIdsSectionSize
);
594 for (auto BI
: BinaryIds
) {
595 uint64_t BILen
= BI
.size();
596 // Write binary id length.
598 // Write binary id data.
599 for (unsigned K
= 0; K
< BILen
; K
++)
601 // Write padding if necessary.
602 uint64_t PaddingSize
= alignToPowerOf2(BILen
, sizeof(uint64_t)) - BILen
;
603 for (unsigned K
= 0; K
< PaddingSize
; K
++)
607 uint64_t TemporalProfTracesSectionStart
= 0;
608 if (static_cast<bool>(ProfileKind
& InstrProfKind::TemporalProfile
)) {
609 TemporalProfTracesSectionStart
= OS
.tell();
610 OS
.write(TemporalProfTraces
.size());
611 OS
.write(TemporalProfTraceStreamSize
);
612 for (auto &Trace
: TemporalProfTraces
) {
613 OS
.write(Trace
.Weight
);
614 OS
.write(Trace
.FunctionNameRefs
.size());
615 for (auto &NameRef
: Trace
.FunctionNameRefs
)
620 // Allocate space for data to be serialized out.
621 std::unique_ptr
<IndexedInstrProf::Summary
> TheSummary
=
622 IndexedInstrProf::allocSummary(SummarySize
);
623 // Compute the Summary and copy the data to the data
624 // structure to be serialized out (to disk or buffer).
625 std::unique_ptr
<ProfileSummary
> PS
= ISB
.getSummary();
626 setSummary(TheSummary
.get(), *PS
);
627 InfoObj
->SummaryBuilder
= nullptr;
629 // For Context Sensitive summary.
630 std::unique_ptr
<IndexedInstrProf::Summary
> TheCSSummary
= nullptr;
631 if (static_cast<bool>(ProfileKind
& InstrProfKind::ContextSensitive
)) {
632 TheCSSummary
= IndexedInstrProf::allocSummary(SummarySize
);
633 std::unique_ptr
<ProfileSummary
> CSPS
= CSISB
.getSummary();
634 setSummary(TheCSSummary
.get(), *CSPS
);
636 InfoObj
->CSSummaryBuilder
= nullptr;
638 // Now do the final patch:
639 PatchItem PatchItems
[] = {
640 // Patch the Header.HashOffset field.
641 {HashTableStartFieldOffset
, &HashTableStart
, 1},
642 // Patch the Header.MemProfOffset (=0 for profiles without MemProf
644 {MemProfSectionOffset
, &MemProfSectionStart
, 1},
645 // Patch the Header.BinaryIdSectionOffset.
646 {BinaryIdSectionOffset
, &BinaryIdSectionStart
, 1},
647 // Patch the Header.TemporalProfTracesOffset (=0 for profiles without
649 {TemporalProfTracesOffset
, &TemporalProfTracesSectionStart
, 1},
650 // Patch the summary data.
651 {SummaryOffset
, reinterpret_cast<uint64_t *>(TheSummary
.get()),
652 (int)(SummarySize
/ sizeof(uint64_t))},
653 {CSSummaryOffset
, reinterpret_cast<uint64_t *>(TheCSSummary
.get()),
654 (int)CSSummarySize
}};
656 OS
.patch(PatchItems
, std::size(PatchItems
));
658 for (const auto &I
: FunctionData
)
659 for (const auto &F
: I
.getValue())
660 if (Error E
= validateRecord(F
.second
))
663 return Error::success();
666 Error
InstrProfWriter::write(raw_fd_ostream
&OS
) {
667 // Write the hash table.
669 return writeImpl(POS
);
672 Error
InstrProfWriter::write(raw_string_ostream
&OS
) {
674 return writeImpl(POS
);
677 std::unique_ptr
<MemoryBuffer
> InstrProfWriter::writeBuffer() {
679 raw_string_ostream
OS(Data
);
680 // Write the hash table.
681 if (Error E
= write(OS
))
683 // Return this in an aligned memory buffer.
684 return MemoryBuffer::getMemBufferCopy(Data
);
687 static const char *ValueProfKindStr
[] = {
688 #define VALUE_PROF_KIND(Enumerator, Value, Descr) #Enumerator,
689 #include "llvm/ProfileData/InstrProfData.inc"
692 Error
InstrProfWriter::validateRecord(const InstrProfRecord
&Func
) {
693 for (uint32_t VK
= 0; VK
<= IPVK_Last
; VK
++) {
694 uint32_t NS
= Func
.getNumValueSites(VK
);
697 for (uint32_t S
= 0; S
< NS
; S
++) {
698 uint32_t ND
= Func
.getNumValueDataForSite(VK
, S
);
699 std::unique_ptr
<InstrProfValueData
[]> VD
= Func
.getValueForSite(VK
, S
);
700 DenseSet
<uint64_t> SeenValues
;
701 for (uint32_t I
= 0; I
< ND
; I
++)
702 if ((VK
!= IPVK_IndirectCallTarget
) && !SeenValues
.insert(VD
[I
].Value
).second
)
703 return make_error
<InstrProfError
>(instrprof_error::invalid_prof
);
707 return Error::success();
710 void InstrProfWriter::writeRecordInText(StringRef Name
, uint64_t Hash
,
711 const InstrProfRecord
&Func
,
712 InstrProfSymtab
&Symtab
,
713 raw_fd_ostream
&OS
) {
715 OS
<< "# Func Hash:\n" << Hash
<< "\n";
716 OS
<< "# Num Counters:\n" << Func
.Counts
.size() << "\n";
717 OS
<< "# Counter Values:\n";
718 for (uint64_t Count
: Func
.Counts
)
721 if (Func
.BitmapBytes
.size() > 0) {
722 OS
<< "# Num Bitmap Bytes:\n$" << Func
.BitmapBytes
.size() << "\n";
723 OS
<< "# Bitmap Byte Values:\n";
724 for (uint8_t Byte
: Func
.BitmapBytes
) {
732 uint32_t NumValueKinds
= Func
.getNumValueKinds();
733 if (!NumValueKinds
) {
738 OS
<< "# Num Value Kinds:\n" << Func
.getNumValueKinds() << "\n";
739 for (uint32_t VK
= 0; VK
< IPVK_Last
+ 1; VK
++) {
740 uint32_t NS
= Func
.getNumValueSites(VK
);
743 OS
<< "# ValueKind = " << ValueProfKindStr
[VK
] << ":\n" << VK
<< "\n";
744 OS
<< "# NumValueSites:\n" << NS
<< "\n";
745 for (uint32_t S
= 0; S
< NS
; S
++) {
746 uint32_t ND
= Func
.getNumValueDataForSite(VK
, S
);
748 std::unique_ptr
<InstrProfValueData
[]> VD
= Func
.getValueForSite(VK
, S
);
749 for (uint32_t I
= 0; I
< ND
; I
++) {
750 if (VK
== IPVK_IndirectCallTarget
)
751 OS
<< Symtab
.getFuncOrVarNameIfDefined(VD
[I
].Value
) << ":"
752 << VD
[I
].Count
<< "\n";
754 OS
<< VD
[I
].Value
<< ":" << VD
[I
].Count
<< "\n";
762 Error
InstrProfWriter::writeText(raw_fd_ostream
&OS
) {
763 // Check CS first since it implies an IR level profile.
764 if (static_cast<bool>(ProfileKind
& InstrProfKind::ContextSensitive
))
765 OS
<< "# CSIR level Instrumentation Flag\n:csir\n";
766 else if (static_cast<bool>(ProfileKind
& InstrProfKind::IRInstrumentation
))
767 OS
<< "# IR level Instrumentation Flag\n:ir\n";
769 if (static_cast<bool>(ProfileKind
&
770 InstrProfKind::FunctionEntryInstrumentation
))
771 OS
<< "# Always instrument the function entry block\n:entry_first\n";
772 if (static_cast<bool>(ProfileKind
& InstrProfKind::SingleByteCoverage
))
773 OS
<< "# Instrument block coverage\n:single_byte_coverage\n";
774 InstrProfSymtab Symtab
;
776 using FuncPair
= detail::DenseMapPair
<uint64_t, InstrProfRecord
>;
777 using RecordType
= std::pair
<StringRef
, FuncPair
>;
778 SmallVector
<RecordType
, 4> OrderedFuncData
;
780 for (const auto &I
: FunctionData
) {
781 if (shouldEncodeData(I
.getValue())) {
782 if (Error E
= Symtab
.addFuncName(I
.getKey()))
784 for (const auto &Func
: I
.getValue())
785 OrderedFuncData
.push_back(std::make_pair(I
.getKey(), Func
));
789 if (static_cast<bool>(ProfileKind
& InstrProfKind::TemporalProfile
))
790 writeTextTemporalProfTraceData(OS
, Symtab
);
792 llvm::sort(OrderedFuncData
, [](const RecordType
&A
, const RecordType
&B
) {
793 return std::tie(A
.first
, A
.second
.first
) <
794 std::tie(B
.first
, B
.second
.first
);
797 for (const auto &record
: OrderedFuncData
) {
798 const StringRef
&Name
= record
.first
;
799 const FuncPair
&Func
= record
.second
;
800 writeRecordInText(Name
, Func
.first
, Func
.second
, Symtab
, OS
);
803 for (const auto &record
: OrderedFuncData
) {
804 const FuncPair
&Func
= record
.second
;
805 if (Error E
= validateRecord(Func
.second
))
809 return Error::success();
812 void InstrProfWriter::writeTextTemporalProfTraceData(raw_fd_ostream
&OS
,
813 InstrProfSymtab
&Symtab
) {
814 OS
<< ":temporal_prof_traces\n";
815 OS
<< "# Num Temporal Profile Traces:\n" << TemporalProfTraces
.size() << "\n";
816 OS
<< "# Temporal Profile Trace Stream Size:\n"
817 << TemporalProfTraceStreamSize
<< "\n";
818 for (auto &Trace
: TemporalProfTraces
) {
819 OS
<< "# Weight:\n" << Trace
.Weight
<< "\n";
820 for (auto &NameRef
: Trace
.FunctionNameRefs
)
821 OS
<< Symtab
.getFuncOrVarName(NameRef
) << ",";