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/StringRef.h"
17 #include "llvm/IR/ProfileSummary.h"
18 #include "llvm/ProfileData/InstrProf.h"
19 #include "llvm/ProfileData/ProfileCommon.h"
20 #include "llvm/Support/Endian.h"
21 #include "llvm/Support/EndianStream.h"
22 #include "llvm/Support/Error.h"
23 #include "llvm/Support/MemoryBuffer.h"
24 #include "llvm/Support/OnDiskHashTable.h"
25 #include "llvm/Support/raw_ostream.h"
36 // A struct to define how the data stream should be patched. For Indexed
37 // profiling, only uint64_t data type is needed.
39 uint64_t Pos
; // Where to patch.
40 uint64_t *D
; // Pointer to an array of source data.
41 int N
; // Number of elements in \c D array.
46 // A wrapper class to abstract writer stream with support of bytes
50 ProfOStream(raw_fd_ostream
&FD
)
51 : IsFDOStream(true), OS(FD
), LE(FD
, support::little
) {}
52 ProfOStream(raw_string_ostream
&STR
)
53 : IsFDOStream(false), OS(STR
), LE(STR
, support::little
) {}
55 uint64_t tell() { return OS
.tell(); }
56 void write(uint64_t V
) { LE
.write
<uint64_t>(V
); }
58 // \c patch can only be called when all data is written and flushed.
59 // For raw_string_ostream, the patch is done on the target string
60 // directly and it won't be reflected in the stream's internal buffer.
61 void patch(PatchItem
*P
, int NItems
) {
62 using namespace support
;
65 raw_fd_ostream
&FDOStream
= static_cast<raw_fd_ostream
&>(OS
);
66 for (int K
= 0; K
< NItems
; K
++) {
67 FDOStream
.seek(P
[K
].Pos
);
68 for (int I
= 0; I
< P
[K
].N
; I
++)
72 raw_string_ostream
&SOStream
= static_cast<raw_string_ostream
&>(OS
);
73 std::string
&Data
= SOStream
.str(); // with flush
74 for (int K
= 0; K
< NItems
; K
++) {
75 for (int I
= 0; I
< P
[K
].N
; I
++) {
76 uint64_t Bytes
= endian::byte_swap
<uint64_t, little
>(P
[K
].D
[I
]);
77 Data
.replace(P
[K
].Pos
+ I
* sizeof(uint64_t), sizeof(uint64_t),
78 (const char *)&Bytes
, sizeof(uint64_t));
84 // If \c OS is an instance of \c raw_fd_ostream, this field will be
85 // true. Otherwise, \c OS will be an raw_string_ostream.
88 support::endian::Writer LE
;
91 class InstrProfRecordWriterTrait
{
93 using key_type
= StringRef
;
94 using key_type_ref
= StringRef
;
96 using data_type
= const InstrProfWriter::ProfilingData
*const;
97 using data_type_ref
= const InstrProfWriter::ProfilingData
*const;
99 using hash_value_type
= uint64_t;
100 using offset_type
= uint64_t;
102 support::endianness ValueProfDataEndianness
= support::little
;
103 InstrProfSummaryBuilder
*SummaryBuilder
;
104 InstrProfSummaryBuilder
*CSSummaryBuilder
;
106 InstrProfRecordWriterTrait() = default;
108 static hash_value_type
ComputeHash(key_type_ref K
) {
109 return IndexedInstrProf::ComputeHash(K
);
112 static std::pair
<offset_type
, offset_type
>
113 EmitKeyDataLength(raw_ostream
&Out
, key_type_ref K
, data_type_ref V
) {
114 using namespace support
;
116 endian::Writer
LE(Out
, little
);
118 offset_type N
= K
.size();
119 LE
.write
<offset_type
>(N
);
122 for (const auto &ProfileData
: *V
) {
123 const InstrProfRecord
&ProfRecord
= ProfileData
.second
;
124 M
+= sizeof(uint64_t); // The function hash
125 M
+= sizeof(uint64_t); // The size of the Counts vector
126 M
+= ProfRecord
.Counts
.size() * sizeof(uint64_t);
129 M
+= ValueProfData::getSize(ProfileData
.second
);
131 LE
.write
<offset_type
>(M
);
133 return std::make_pair(N
, M
);
136 void EmitKey(raw_ostream
&Out
, key_type_ref K
, offset_type N
) {
137 Out
.write(K
.data(), N
);
140 void EmitData(raw_ostream
&Out
, key_type_ref
, data_type_ref V
, offset_type
) {
141 using namespace support
;
143 endian::Writer
LE(Out
, little
);
144 for (const auto &ProfileData
: *V
) {
145 const InstrProfRecord
&ProfRecord
= ProfileData
.second
;
146 if (NamedInstrProfRecord::hasCSFlagInHash(ProfileData
.first
))
147 CSSummaryBuilder
->addRecord(ProfRecord
);
149 SummaryBuilder
->addRecord(ProfRecord
);
151 LE
.write
<uint64_t>(ProfileData
.first
); // Function hash
152 LE
.write
<uint64_t>(ProfRecord
.Counts
.size());
153 for (uint64_t I
: ProfRecord
.Counts
)
154 LE
.write
<uint64_t>(I
);
157 std::unique_ptr
<ValueProfData
> VDataPtr
=
158 ValueProfData::serializeFrom(ProfileData
.second
);
159 uint32_t S
= VDataPtr
->getSize();
160 VDataPtr
->swapBytesFromHost(ValueProfDataEndianness
);
161 Out
.write((const char *)VDataPtr
.get(), S
);
166 } // end namespace llvm
168 InstrProfWriter::InstrProfWriter(bool Sparse
)
169 : Sparse(Sparse
), InfoObj(new InstrProfRecordWriterTrait()) {}
171 InstrProfWriter::~InstrProfWriter() { delete InfoObj
; }
173 // Internal interface for testing purpose only.
174 void InstrProfWriter::setValueProfDataEndianness(
175 support::endianness Endianness
) {
176 InfoObj
->ValueProfDataEndianness
= Endianness
;
179 void InstrProfWriter::setOutputSparse(bool Sparse
) {
180 this->Sparse
= Sparse
;
183 void InstrProfWriter::addRecord(NamedInstrProfRecord
&&I
, uint64_t Weight
,
184 function_ref
<void(Error
)> Warn
) {
187 addRecord(Name
, Hash
, std::move(I
), Weight
, Warn
);
190 void InstrProfWriter::overlapRecord(NamedInstrProfRecord
&&Other
,
191 OverlapStats
&Overlap
,
192 OverlapStats
&FuncLevelOverlap
,
193 const OverlapFuncFilters
&FuncFilter
) {
194 auto Name
= Other
.Name
;
195 auto Hash
= Other
.Hash
;
196 Other
.accumulateCounts(FuncLevelOverlap
.Test
);
197 if (FunctionData
.find(Name
) == FunctionData
.end()) {
198 Overlap
.addOneUnique(FuncLevelOverlap
.Test
);
201 if (FuncLevelOverlap
.Test
.CountSum
< 1.0f
) {
202 Overlap
.Overlap
.NumEntries
+= 1;
205 auto &ProfileDataMap
= FunctionData
[Name
];
207 ProfilingData::iterator Where
;
208 std::tie(Where
, NewFunc
) =
209 ProfileDataMap
.insert(std::make_pair(Hash
, InstrProfRecord()));
211 Overlap
.addOneMismatch(FuncLevelOverlap
.Test
);
214 InstrProfRecord
&Dest
= Where
->second
;
216 uint64_t ValueCutoff
= FuncFilter
.ValueCutoff
;
217 if (!FuncFilter
.NameFilter
.empty() &&
218 Name
.find(FuncFilter
.NameFilter
) != Name
.npos
)
221 Dest
.overlap(Other
, Overlap
, FuncLevelOverlap
, ValueCutoff
);
224 void InstrProfWriter::addRecord(StringRef Name
, uint64_t Hash
,
225 InstrProfRecord
&&I
, uint64_t Weight
,
226 function_ref
<void(Error
)> Warn
) {
227 auto &ProfileDataMap
= FunctionData
[Name
];
230 ProfilingData::iterator Where
;
231 std::tie(Where
, NewFunc
) =
232 ProfileDataMap
.insert(std::make_pair(Hash
, InstrProfRecord()));
233 InstrProfRecord
&Dest
= Where
->second
;
235 auto MapWarn
= [&](instrprof_error E
) {
236 Warn(make_error
<InstrProfError
>(E
));
240 // We've never seen a function with this name and hash, add it.
243 Dest
.scale(Weight
, MapWarn
);
245 // We're updating a function we've seen before.
246 Dest
.merge(I
, Weight
, MapWarn
);
249 Dest
.sortValueData();
252 void InstrProfWriter::mergeRecordsFromWriter(InstrProfWriter
&&IPW
,
253 function_ref
<void(Error
)> Warn
) {
254 for (auto &I
: IPW
.FunctionData
)
255 for (auto &Func
: I
.getValue())
256 addRecord(I
.getKey(), Func
.first
, std::move(Func
.second
), 1, Warn
);
259 bool InstrProfWriter::shouldEncodeData(const ProfilingData
&PD
) {
262 for (const auto &Func
: PD
) {
263 const InstrProfRecord
&IPR
= Func
.second
;
264 if (llvm::any_of(IPR
.Counts
, [](uint64_t Count
) { return Count
> 0; }))
270 static void setSummary(IndexedInstrProf::Summary
*TheSummary
,
271 ProfileSummary
&PS
) {
272 using namespace IndexedInstrProf
;
274 std::vector
<ProfileSummaryEntry
> &Res
= PS
.getDetailedSummary();
275 TheSummary
->NumSummaryFields
= Summary::NumKinds
;
276 TheSummary
->NumCutoffEntries
= Res
.size();
277 TheSummary
->set(Summary::MaxFunctionCount
, PS
.getMaxFunctionCount());
278 TheSummary
->set(Summary::MaxBlockCount
, PS
.getMaxCount());
279 TheSummary
->set(Summary::MaxInternalBlockCount
, PS
.getMaxInternalCount());
280 TheSummary
->set(Summary::TotalBlockCount
, PS
.getTotalCount());
281 TheSummary
->set(Summary::TotalNumBlocks
, PS
.getNumCounts());
282 TheSummary
->set(Summary::TotalNumFunctions
, PS
.getNumFunctions());
283 for (unsigned I
= 0; I
< Res
.size(); I
++)
284 TheSummary
->setEntry(I
, Res
[I
]);
287 void InstrProfWriter::writeImpl(ProfOStream
&OS
) {
288 using namespace IndexedInstrProf
;
290 OnDiskChainedHashTableGenerator
<InstrProfRecordWriterTrait
> Generator
;
292 InstrProfSummaryBuilder
ISB(ProfileSummaryBuilder::DefaultCutoffs
);
293 InfoObj
->SummaryBuilder
= &ISB
;
294 InstrProfSummaryBuilder
CSISB(ProfileSummaryBuilder::DefaultCutoffs
);
295 InfoObj
->CSSummaryBuilder
= &CSISB
;
297 // Populate the hash table generator.
298 for (const auto &I
: FunctionData
)
299 if (shouldEncodeData(I
.getValue()))
300 Generator
.insert(I
.getKey(), &I
.getValue());
302 IndexedInstrProf::Header Header
;
303 Header
.Magic
= IndexedInstrProf::Magic
;
304 Header
.Version
= IndexedInstrProf::ProfVersion::CurrentVersion
;
305 if (ProfileKind
== PF_IRLevel
)
306 Header
.Version
|= VARIANT_MASK_IR_PROF
;
307 if (ProfileKind
== PF_IRLevelWithCS
) {
308 Header
.Version
|= VARIANT_MASK_IR_PROF
;
309 Header
.Version
|= VARIANT_MASK_CSIR_PROF
;
312 Header
.HashType
= static_cast<uint64_t>(IndexedInstrProf::HashType
);
313 Header
.HashOffset
= 0;
314 int N
= sizeof(IndexedInstrProf::Header
) / sizeof(uint64_t);
316 // Only write out all the fields except 'HashOffset'. We need
317 // to remember the offset of that field to allow back patching
319 for (int I
= 0; I
< N
- 1; I
++)
320 OS
.write(reinterpret_cast<uint64_t *>(&Header
)[I
]);
322 // Save the location of Header.HashOffset field in \c OS.
323 uint64_t HashTableStartFieldOffset
= OS
.tell();
324 // Reserve the space for HashOffset field.
327 // Reserve space to write profile summary data.
328 uint32_t NumEntries
= ProfileSummaryBuilder::DefaultCutoffs
.size();
329 uint32_t SummarySize
= Summary::getSize(Summary::NumKinds
, NumEntries
);
330 // Remember the summary offset.
331 uint64_t SummaryOffset
= OS
.tell();
332 for (unsigned I
= 0; I
< SummarySize
/ sizeof(uint64_t); I
++)
334 uint64_t CSSummaryOffset
= 0;
335 uint64_t CSSummarySize
= 0;
336 if (ProfileKind
== PF_IRLevelWithCS
) {
337 CSSummaryOffset
= OS
.tell();
338 CSSummarySize
= SummarySize
/ sizeof(uint64_t);
339 for (unsigned I
= 0; I
< CSSummarySize
; I
++)
343 // Write the hash table.
344 uint64_t HashTableStart
= Generator
.Emit(OS
.OS
, *InfoObj
);
346 // Allocate space for data to be serialized out.
347 std::unique_ptr
<IndexedInstrProf::Summary
> TheSummary
=
348 IndexedInstrProf::allocSummary(SummarySize
);
349 // Compute the Summary and copy the data to the data
350 // structure to be serialized out (to disk or buffer).
351 std::unique_ptr
<ProfileSummary
> PS
= ISB
.getSummary();
352 setSummary(TheSummary
.get(), *PS
);
353 InfoObj
->SummaryBuilder
= nullptr;
355 // For Context Sensitive summary.
356 std::unique_ptr
<IndexedInstrProf::Summary
> TheCSSummary
= nullptr;
357 if (ProfileKind
== PF_IRLevelWithCS
) {
358 TheCSSummary
= IndexedInstrProf::allocSummary(SummarySize
);
359 std::unique_ptr
<ProfileSummary
> CSPS
= CSISB
.getSummary();
360 setSummary(TheCSSummary
.get(), *CSPS
);
362 InfoObj
->CSSummaryBuilder
= nullptr;
364 // Now do the final patch:
365 PatchItem PatchItems
[] = {
366 // Patch the Header.HashOffset field.
367 {HashTableStartFieldOffset
, &HashTableStart
, 1},
368 // Patch the summary data.
369 {SummaryOffset
, reinterpret_cast<uint64_t *>(TheSummary
.get()),
370 (int)(SummarySize
/ sizeof(uint64_t))},
371 {CSSummaryOffset
, reinterpret_cast<uint64_t *>(TheCSSummary
.get()),
372 (int)CSSummarySize
}};
374 OS
.patch(PatchItems
, sizeof(PatchItems
) / sizeof(*PatchItems
));
377 void InstrProfWriter::write(raw_fd_ostream
&OS
) {
378 // Write the hash table.
383 std::unique_ptr
<MemoryBuffer
> InstrProfWriter::writeBuffer() {
385 raw_string_ostream
OS(Data
);
387 // Write the hash table.
389 // Return this in an aligned memory buffer.
390 return MemoryBuffer::getMemBufferCopy(Data
);
393 static const char *ValueProfKindStr
[] = {
394 #define VALUE_PROF_KIND(Enumerator, Value, Descr) #Enumerator,
395 #include "llvm/ProfileData/InstrProfData.inc"
398 void InstrProfWriter::writeRecordInText(StringRef Name
, uint64_t Hash
,
399 const InstrProfRecord
&Func
,
400 InstrProfSymtab
&Symtab
,
401 raw_fd_ostream
&OS
) {
403 OS
<< "# Func Hash:\n" << Hash
<< "\n";
404 OS
<< "# Num Counters:\n" << Func
.Counts
.size() << "\n";
405 OS
<< "# Counter Values:\n";
406 for (uint64_t Count
: Func
.Counts
)
409 uint32_t NumValueKinds
= Func
.getNumValueKinds();
410 if (!NumValueKinds
) {
415 OS
<< "# Num Value Kinds:\n" << Func
.getNumValueKinds() << "\n";
416 for (uint32_t VK
= 0; VK
< IPVK_Last
+ 1; VK
++) {
417 uint32_t NS
= Func
.getNumValueSites(VK
);
420 OS
<< "# ValueKind = " << ValueProfKindStr
[VK
] << ":\n" << VK
<< "\n";
421 OS
<< "# NumValueSites:\n" << NS
<< "\n";
422 for (uint32_t S
= 0; S
< NS
; S
++) {
423 uint32_t ND
= Func
.getNumValueDataForSite(VK
, S
);
425 std::unique_ptr
<InstrProfValueData
[]> VD
= Func
.getValueForSite(VK
, S
);
426 for (uint32_t I
= 0; I
< ND
; I
++) {
427 if (VK
== IPVK_IndirectCallTarget
)
428 OS
<< Symtab
.getFuncNameOrExternalSymbol(VD
[I
].Value
) << ":"
429 << VD
[I
].Count
<< "\n";
431 OS
<< VD
[I
].Value
<< ":" << VD
[I
].Count
<< "\n";
439 Error
InstrProfWriter::writeText(raw_fd_ostream
&OS
) {
440 if (ProfileKind
== PF_IRLevel
)
441 OS
<< "# IR level Instrumentation Flag\n:ir\n";
442 else if (ProfileKind
== PF_IRLevelWithCS
)
443 OS
<< "# CSIR level Instrumentation Flag\n:csir\n";
444 InstrProfSymtab Symtab
;
446 using FuncPair
= detail::DenseMapPair
<uint64_t, InstrProfRecord
>;
447 using RecordType
= std::pair
<StringRef
, FuncPair
>;
448 SmallVector
<RecordType
, 4> OrderedFuncData
;
450 for (const auto &I
: FunctionData
) {
451 if (shouldEncodeData(I
.getValue())) {
452 if (Error E
= Symtab
.addFuncName(I
.getKey()))
454 for (const auto &Func
: I
.getValue())
455 OrderedFuncData
.push_back(std::make_pair(I
.getKey(), Func
));
459 llvm::sort(OrderedFuncData
, [](const RecordType
&A
, const RecordType
&B
) {
460 return std::tie(A
.first
, A
.second
.first
) <
461 std::tie(B
.first
, B
.second
.first
);
464 for (const auto &record
: OrderedFuncData
) {
465 const StringRef
&Name
= record
.first
;
466 const FuncPair
&Func
= record
.second
;
467 writeRecordInText(Name
, Func
.first
, Func
.second
, Symtab
, OS
);
470 return Error::success();