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
;
105 InstrProfRecordWriterTrait() = default;
107 static hash_value_type
ComputeHash(key_type_ref K
) {
108 return IndexedInstrProf::ComputeHash(K
);
111 static std::pair
<offset_type
, offset_type
>
112 EmitKeyDataLength(raw_ostream
&Out
, key_type_ref K
, data_type_ref V
) {
113 using namespace support
;
115 endian::Writer
LE(Out
, little
);
117 offset_type N
= K
.size();
118 LE
.write
<offset_type
>(N
);
121 for (const auto &ProfileData
: *V
) {
122 const InstrProfRecord
&ProfRecord
= ProfileData
.second
;
123 M
+= sizeof(uint64_t); // The function hash
124 M
+= sizeof(uint64_t); // The size of the Counts vector
125 M
+= ProfRecord
.Counts
.size() * sizeof(uint64_t);
128 M
+= ValueProfData::getSize(ProfileData
.second
);
130 LE
.write
<offset_type
>(M
);
132 return std::make_pair(N
, M
);
135 void EmitKey(raw_ostream
&Out
, key_type_ref K
, offset_type N
) {
136 Out
.write(K
.data(), N
);
139 void EmitData(raw_ostream
&Out
, key_type_ref
, data_type_ref V
, offset_type
) {
140 using namespace support
;
142 endian::Writer
LE(Out
, little
);
143 for (const auto &ProfileData
: *V
) {
144 const InstrProfRecord
&ProfRecord
= ProfileData
.second
;
145 SummaryBuilder
->addRecord(ProfRecord
);
147 LE
.write
<uint64_t>(ProfileData
.first
); // Function hash
148 LE
.write
<uint64_t>(ProfRecord
.Counts
.size());
149 for (uint64_t I
: ProfRecord
.Counts
)
150 LE
.write
<uint64_t>(I
);
153 std::unique_ptr
<ValueProfData
> VDataPtr
=
154 ValueProfData::serializeFrom(ProfileData
.second
);
155 uint32_t S
= VDataPtr
->getSize();
156 VDataPtr
->swapBytesFromHost(ValueProfDataEndianness
);
157 Out
.write((const char *)VDataPtr
.get(), S
);
162 } // end namespace llvm
164 InstrProfWriter::InstrProfWriter(bool Sparse
)
165 : Sparse(Sparse
), InfoObj(new InstrProfRecordWriterTrait()) {}
167 InstrProfWriter::~InstrProfWriter() { delete InfoObj
; }
169 // Internal interface for testing purpose only.
170 void InstrProfWriter::setValueProfDataEndianness(
171 support::endianness Endianness
) {
172 InfoObj
->ValueProfDataEndianness
= Endianness
;
175 void InstrProfWriter::setOutputSparse(bool Sparse
) {
176 this->Sparse
= Sparse
;
179 void InstrProfWriter::addRecord(NamedInstrProfRecord
&&I
, uint64_t Weight
,
180 function_ref
<void(Error
)> Warn
) {
183 addRecord(Name
, Hash
, std::move(I
), Weight
, Warn
);
186 void InstrProfWriter::addRecord(StringRef Name
, uint64_t Hash
,
187 InstrProfRecord
&&I
, uint64_t Weight
,
188 function_ref
<void(Error
)> Warn
) {
189 auto &ProfileDataMap
= FunctionData
[Name
];
192 ProfilingData::iterator Where
;
193 std::tie(Where
, NewFunc
) =
194 ProfileDataMap
.insert(std::make_pair(Hash
, InstrProfRecord()));
195 InstrProfRecord
&Dest
= Where
->second
;
197 auto MapWarn
= [&](instrprof_error E
) {
198 Warn(make_error
<InstrProfError
>(E
));
202 // We've never seen a function with this name and hash, add it.
205 Dest
.scale(Weight
, MapWarn
);
207 // We're updating a function we've seen before.
208 Dest
.merge(I
, Weight
, MapWarn
);
211 Dest
.sortValueData();
214 void InstrProfWriter::mergeRecordsFromWriter(InstrProfWriter
&&IPW
,
215 function_ref
<void(Error
)> Warn
) {
216 for (auto &I
: IPW
.FunctionData
)
217 for (auto &Func
: I
.getValue())
218 addRecord(I
.getKey(), Func
.first
, std::move(Func
.second
), 1, Warn
);
221 bool InstrProfWriter::shouldEncodeData(const ProfilingData
&PD
) {
224 for (const auto &Func
: PD
) {
225 const InstrProfRecord
&IPR
= Func
.second
;
226 if (llvm::any_of(IPR
.Counts
, [](uint64_t Count
) { return Count
> 0; }))
232 static void setSummary(IndexedInstrProf::Summary
*TheSummary
,
233 ProfileSummary
&PS
) {
234 using namespace IndexedInstrProf
;
236 std::vector
<ProfileSummaryEntry
> &Res
= PS
.getDetailedSummary();
237 TheSummary
->NumSummaryFields
= Summary::NumKinds
;
238 TheSummary
->NumCutoffEntries
= Res
.size();
239 TheSummary
->set(Summary::MaxFunctionCount
, PS
.getMaxFunctionCount());
240 TheSummary
->set(Summary::MaxBlockCount
, PS
.getMaxCount());
241 TheSummary
->set(Summary::MaxInternalBlockCount
, PS
.getMaxInternalCount());
242 TheSummary
->set(Summary::TotalBlockCount
, PS
.getTotalCount());
243 TheSummary
->set(Summary::TotalNumBlocks
, PS
.getNumCounts());
244 TheSummary
->set(Summary::TotalNumFunctions
, PS
.getNumFunctions());
245 for (unsigned I
= 0; I
< Res
.size(); I
++)
246 TheSummary
->setEntry(I
, Res
[I
]);
249 void InstrProfWriter::writeImpl(ProfOStream
&OS
) {
250 using namespace IndexedInstrProf
;
252 OnDiskChainedHashTableGenerator
<InstrProfRecordWriterTrait
> Generator
;
254 InstrProfSummaryBuilder
ISB(ProfileSummaryBuilder::DefaultCutoffs
);
255 InfoObj
->SummaryBuilder
= &ISB
;
257 // Populate the hash table generator.
258 for (const auto &I
: FunctionData
)
259 if (shouldEncodeData(I
.getValue()))
260 Generator
.insert(I
.getKey(), &I
.getValue());
262 IndexedInstrProf::Header Header
;
263 Header
.Magic
= IndexedInstrProf::Magic
;
264 Header
.Version
= IndexedInstrProf::ProfVersion::CurrentVersion
;
265 if (ProfileKind
== PF_IRLevel
)
266 Header
.Version
|= VARIANT_MASK_IR_PROF
;
268 Header
.HashType
= static_cast<uint64_t>(IndexedInstrProf::HashType
);
269 Header
.HashOffset
= 0;
270 int N
= sizeof(IndexedInstrProf::Header
) / sizeof(uint64_t);
272 // Only write out all the fields except 'HashOffset'. We need
273 // to remember the offset of that field to allow back patching
275 for (int I
= 0; I
< N
- 1; I
++)
276 OS
.write(reinterpret_cast<uint64_t *>(&Header
)[I
]);
278 // Save the location of Header.HashOffset field in \c OS.
279 uint64_t HashTableStartFieldOffset
= OS
.tell();
280 // Reserve the space for HashOffset field.
283 // Reserve space to write profile summary data.
284 uint32_t NumEntries
= ProfileSummaryBuilder::DefaultCutoffs
.size();
285 uint32_t SummarySize
= Summary::getSize(Summary::NumKinds
, NumEntries
);
286 // Remember the summary offset.
287 uint64_t SummaryOffset
= OS
.tell();
288 for (unsigned I
= 0; I
< SummarySize
/ sizeof(uint64_t); I
++)
291 // Write the hash table.
292 uint64_t HashTableStart
= Generator
.Emit(OS
.OS
, *InfoObj
);
294 // Allocate space for data to be serialized out.
295 std::unique_ptr
<IndexedInstrProf::Summary
> TheSummary
=
296 IndexedInstrProf::allocSummary(SummarySize
);
297 // Compute the Summary and copy the data to the data
298 // structure to be serialized out (to disk or buffer).
299 std::unique_ptr
<ProfileSummary
> PS
= ISB
.getSummary();
300 setSummary(TheSummary
.get(), *PS
);
301 InfoObj
->SummaryBuilder
= nullptr;
303 // Now do the final patch:
304 PatchItem PatchItems
[] = {
305 // Patch the Header.HashOffset field.
306 {HashTableStartFieldOffset
, &HashTableStart
, 1},
307 // Patch the summary data.
308 {SummaryOffset
, reinterpret_cast<uint64_t *>(TheSummary
.get()),
309 (int)(SummarySize
/ sizeof(uint64_t))}};
310 OS
.patch(PatchItems
, sizeof(PatchItems
) / sizeof(*PatchItems
));
313 void InstrProfWriter::write(raw_fd_ostream
&OS
) {
314 // Write the hash table.
319 std::unique_ptr
<MemoryBuffer
> InstrProfWriter::writeBuffer() {
321 raw_string_ostream
OS(Data
);
323 // Write the hash table.
325 // Return this in an aligned memory buffer.
326 return MemoryBuffer::getMemBufferCopy(Data
);
329 static const char *ValueProfKindStr
[] = {
330 #define VALUE_PROF_KIND(Enumerator, Value) #Enumerator,
331 #include "llvm/ProfileData/InstrProfData.inc"
334 void InstrProfWriter::writeRecordInText(StringRef Name
, uint64_t Hash
,
335 const InstrProfRecord
&Func
,
336 InstrProfSymtab
&Symtab
,
337 raw_fd_ostream
&OS
) {
339 OS
<< "# Func Hash:\n" << Hash
<< "\n";
340 OS
<< "# Num Counters:\n" << Func
.Counts
.size() << "\n";
341 OS
<< "# Counter Values:\n";
342 for (uint64_t Count
: Func
.Counts
)
345 uint32_t NumValueKinds
= Func
.getNumValueKinds();
346 if (!NumValueKinds
) {
351 OS
<< "# Num Value Kinds:\n" << Func
.getNumValueKinds() << "\n";
352 for (uint32_t VK
= 0; VK
< IPVK_Last
+ 1; VK
++) {
353 uint32_t NS
= Func
.getNumValueSites(VK
);
356 OS
<< "# ValueKind = " << ValueProfKindStr
[VK
] << ":\n" << VK
<< "\n";
357 OS
<< "# NumValueSites:\n" << NS
<< "\n";
358 for (uint32_t S
= 0; S
< NS
; S
++) {
359 uint32_t ND
= Func
.getNumValueDataForSite(VK
, S
);
361 std::unique_ptr
<InstrProfValueData
[]> VD
= Func
.getValueForSite(VK
, S
);
362 for (uint32_t I
= 0; I
< ND
; I
++) {
363 if (VK
== IPVK_IndirectCallTarget
)
364 OS
<< Symtab
.getFuncNameOrExternalSymbol(VD
[I
].Value
) << ":"
365 << VD
[I
].Count
<< "\n";
367 OS
<< VD
[I
].Value
<< ":" << VD
[I
].Count
<< "\n";
375 Error
InstrProfWriter::writeText(raw_fd_ostream
&OS
) {
376 if (ProfileKind
== PF_IRLevel
)
377 OS
<< "# IR level Instrumentation Flag\n:ir\n";
378 InstrProfSymtab Symtab
;
379 for (const auto &I
: FunctionData
)
380 if (shouldEncodeData(I
.getValue()))
381 if (Error E
= Symtab
.addFuncName(I
.getKey()))
384 for (const auto &I
: FunctionData
)
385 if (shouldEncodeData(I
.getValue()))
386 for (const auto &Func
: I
.getValue())
387 writeRecordInText(I
.getKey(), Func
.first
, Func
.second
, Symtab
, OS
);
388 return Error::success();