1 //===- InstrProfWriter.cpp - Instrumented profiling writer ----------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file contains support for writing profiling data for clang's
11 // instrumentation based PGO and coverage.
13 //===----------------------------------------------------------------------===//
15 #include "llvm/ProfileData/InstrProfWriter.h"
16 #include "llvm/ADT/STLExtras.h"
17 #include "llvm/ADT/StringRef.h"
18 #include "llvm/IR/ProfileSummary.h"
19 #include "llvm/ProfileData/InstrProf.h"
20 #include "llvm/ProfileData/ProfileCommon.h"
21 #include "llvm/Support/Endian.h"
22 #include "llvm/Support/EndianStream.h"
23 #include "llvm/Support/Error.h"
24 #include "llvm/Support/MemoryBuffer.h"
25 #include "llvm/Support/OnDiskHashTable.h"
26 #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
, support::little
) {}
53 ProfOStream(raw_string_ostream
&STR
)
54 : IsFDOStream(false), OS(STR
), LE(STR
, support::little
) {}
56 uint64_t tell() { return OS
.tell(); }
57 void write(uint64_t V
) { LE
.write
<uint64_t>(V
); }
59 // \c patch can only be called when all data is written and flushed.
60 // For raw_string_ostream, the patch is done on the target string
61 // directly and it won't be reflected in the stream's internal buffer.
62 void patch(PatchItem
*P
, int NItems
) {
63 using namespace support
;
66 raw_fd_ostream
&FDOStream
= static_cast<raw_fd_ostream
&>(OS
);
67 for (int K
= 0; K
< NItems
; K
++) {
68 FDOStream
.seek(P
[K
].Pos
);
69 for (int I
= 0; I
< P
[K
].N
; I
++)
73 raw_string_ostream
&SOStream
= static_cast<raw_string_ostream
&>(OS
);
74 std::string
&Data
= SOStream
.str(); // with flush
75 for (int K
= 0; K
< NItems
; K
++) {
76 for (int I
= 0; I
< P
[K
].N
; I
++) {
77 uint64_t Bytes
= endian::byte_swap
<uint64_t, little
>(P
[K
].D
[I
]);
78 Data
.replace(P
[K
].Pos
+ I
* sizeof(uint64_t), sizeof(uint64_t),
79 (const char *)&Bytes
, sizeof(uint64_t));
85 // If \c OS is an instance of \c raw_fd_ostream, this field will be
86 // true. Otherwise, \c OS will be an raw_string_ostream.
89 support::endian::Writer LE
;
92 class InstrProfRecordWriterTrait
{
94 using key_type
= StringRef
;
95 using key_type_ref
= StringRef
;
97 using data_type
= const InstrProfWriter::ProfilingData
*const;
98 using data_type_ref
= const InstrProfWriter::ProfilingData
*const;
100 using hash_value_type
= uint64_t;
101 using offset_type
= uint64_t;
103 support::endianness ValueProfDataEndianness
= support::little
;
104 InstrProfSummaryBuilder
*SummaryBuilder
;
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 SummaryBuilder
->addRecord(ProfRecord
);
148 LE
.write
<uint64_t>(ProfileData
.first
); // Function hash
149 LE
.write
<uint64_t>(ProfRecord
.Counts
.size());
150 for (uint64_t I
: ProfRecord
.Counts
)
151 LE
.write
<uint64_t>(I
);
154 std::unique_ptr
<ValueProfData
> VDataPtr
=
155 ValueProfData::serializeFrom(ProfileData
.second
);
156 uint32_t S
= VDataPtr
->getSize();
157 VDataPtr
->swapBytesFromHost(ValueProfDataEndianness
);
158 Out
.write((const char *)VDataPtr
.get(), S
);
163 } // end namespace llvm
165 InstrProfWriter::InstrProfWriter(bool Sparse
)
166 : Sparse(Sparse
), InfoObj(new InstrProfRecordWriterTrait()) {}
168 InstrProfWriter::~InstrProfWriter() { delete InfoObj
; }
170 // Internal interface for testing purpose only.
171 void InstrProfWriter::setValueProfDataEndianness(
172 support::endianness Endianness
) {
173 InfoObj
->ValueProfDataEndianness
= Endianness
;
176 void InstrProfWriter::setOutputSparse(bool Sparse
) {
177 this->Sparse
= Sparse
;
180 void InstrProfWriter::addRecord(NamedInstrProfRecord
&&I
, uint64_t Weight
,
181 function_ref
<void(Error
)> Warn
) {
184 addRecord(Name
, Hash
, std::move(I
), Weight
, Warn
);
187 void InstrProfWriter::addRecord(StringRef Name
, uint64_t Hash
,
188 InstrProfRecord
&&I
, uint64_t Weight
,
189 function_ref
<void(Error
)> Warn
) {
190 auto &ProfileDataMap
= FunctionData
[Name
];
193 ProfilingData::iterator Where
;
194 std::tie(Where
, NewFunc
) =
195 ProfileDataMap
.insert(std::make_pair(Hash
, InstrProfRecord()));
196 InstrProfRecord
&Dest
= Where
->second
;
198 auto MapWarn
= [&](instrprof_error E
) {
199 Warn(make_error
<InstrProfError
>(E
));
203 // We've never seen a function with this name and hash, add it.
206 Dest
.scale(Weight
, MapWarn
);
208 // We're updating a function we've seen before.
209 Dest
.merge(I
, Weight
, MapWarn
);
212 Dest
.sortValueData();
215 void InstrProfWriter::mergeRecordsFromWriter(InstrProfWriter
&&IPW
,
216 function_ref
<void(Error
)> Warn
) {
217 for (auto &I
: IPW
.FunctionData
)
218 for (auto &Func
: I
.getValue())
219 addRecord(I
.getKey(), Func
.first
, std::move(Func
.second
), 1, Warn
);
222 bool InstrProfWriter::shouldEncodeData(const ProfilingData
&PD
) {
225 for (const auto &Func
: PD
) {
226 const InstrProfRecord
&IPR
= Func
.second
;
227 if (llvm::any_of(IPR
.Counts
, [](uint64_t Count
) { return Count
> 0; }))
233 static void setSummary(IndexedInstrProf::Summary
*TheSummary
,
234 ProfileSummary
&PS
) {
235 using namespace IndexedInstrProf
;
237 std::vector
<ProfileSummaryEntry
> &Res
= PS
.getDetailedSummary();
238 TheSummary
->NumSummaryFields
= Summary::NumKinds
;
239 TheSummary
->NumCutoffEntries
= Res
.size();
240 TheSummary
->set(Summary::MaxFunctionCount
, PS
.getMaxFunctionCount());
241 TheSummary
->set(Summary::MaxBlockCount
, PS
.getMaxCount());
242 TheSummary
->set(Summary::MaxInternalBlockCount
, PS
.getMaxInternalCount());
243 TheSummary
->set(Summary::TotalBlockCount
, PS
.getTotalCount());
244 TheSummary
->set(Summary::TotalNumBlocks
, PS
.getNumCounts());
245 TheSummary
->set(Summary::TotalNumFunctions
, PS
.getNumFunctions());
246 for (unsigned I
= 0; I
< Res
.size(); I
++)
247 TheSummary
->setEntry(I
, Res
[I
]);
250 void InstrProfWriter::writeImpl(ProfOStream
&OS
) {
251 using namespace IndexedInstrProf
;
253 OnDiskChainedHashTableGenerator
<InstrProfRecordWriterTrait
> Generator
;
255 InstrProfSummaryBuilder
ISB(ProfileSummaryBuilder::DefaultCutoffs
);
256 InfoObj
->SummaryBuilder
= &ISB
;
258 // Populate the hash table generator.
259 for (const auto &I
: FunctionData
)
260 if (shouldEncodeData(I
.getValue()))
261 Generator
.insert(I
.getKey(), &I
.getValue());
263 IndexedInstrProf::Header Header
;
264 Header
.Magic
= IndexedInstrProf::Magic
;
265 Header
.Version
= IndexedInstrProf::ProfVersion::CurrentVersion
;
266 if (ProfileKind
== PF_IRLevel
)
267 Header
.Version
|= VARIANT_MASK_IR_PROF
;
269 Header
.HashType
= static_cast<uint64_t>(IndexedInstrProf::HashType
);
270 Header
.HashOffset
= 0;
271 int N
= sizeof(IndexedInstrProf::Header
) / sizeof(uint64_t);
273 // Only write out all the fields except 'HashOffset'. We need
274 // to remember the offset of that field to allow back patching
276 for (int I
= 0; I
< N
- 1; I
++)
277 OS
.write(reinterpret_cast<uint64_t *>(&Header
)[I
]);
279 // Save the location of Header.HashOffset field in \c OS.
280 uint64_t HashTableStartFieldOffset
= OS
.tell();
281 // Reserve the space for HashOffset field.
284 // Reserve space to write profile summary data.
285 uint32_t NumEntries
= ProfileSummaryBuilder::DefaultCutoffs
.size();
286 uint32_t SummarySize
= Summary::getSize(Summary::NumKinds
, NumEntries
);
287 // Remember the summary offset.
288 uint64_t SummaryOffset
= OS
.tell();
289 for (unsigned I
= 0; I
< SummarySize
/ sizeof(uint64_t); I
++)
292 // Write the hash table.
293 uint64_t HashTableStart
= Generator
.Emit(OS
.OS
, *InfoObj
);
295 // Allocate space for data to be serialized out.
296 std::unique_ptr
<IndexedInstrProf::Summary
> TheSummary
=
297 IndexedInstrProf::allocSummary(SummarySize
);
298 // Compute the Summary and copy the data to the data
299 // structure to be serialized out (to disk or buffer).
300 std::unique_ptr
<ProfileSummary
> PS
= ISB
.getSummary();
301 setSummary(TheSummary
.get(), *PS
);
302 InfoObj
->SummaryBuilder
= nullptr;
304 // Now do the final patch:
305 PatchItem PatchItems
[] = {
306 // Patch the Header.HashOffset field.
307 {HashTableStartFieldOffset
, &HashTableStart
, 1},
308 // Patch the summary data.
309 {SummaryOffset
, reinterpret_cast<uint64_t *>(TheSummary
.get()),
310 (int)(SummarySize
/ sizeof(uint64_t))}};
311 OS
.patch(PatchItems
, sizeof(PatchItems
) / sizeof(*PatchItems
));
314 void InstrProfWriter::write(raw_fd_ostream
&OS
) {
315 // Write the hash table.
320 std::unique_ptr
<MemoryBuffer
> InstrProfWriter::writeBuffer() {
322 raw_string_ostream
OS(Data
);
324 // Write the hash table.
326 // Return this in an aligned memory buffer.
327 return MemoryBuffer::getMemBufferCopy(Data
);
330 static const char *ValueProfKindStr
[] = {
331 #define VALUE_PROF_KIND(Enumerator, Value) #Enumerator,
332 #include "llvm/ProfileData/InstrProfData.inc"
335 void InstrProfWriter::writeRecordInText(StringRef Name
, uint64_t Hash
,
336 const InstrProfRecord
&Func
,
337 InstrProfSymtab
&Symtab
,
338 raw_fd_ostream
&OS
) {
340 OS
<< "# Func Hash:\n" << Hash
<< "\n";
341 OS
<< "# Num Counters:\n" << Func
.Counts
.size() << "\n";
342 OS
<< "# Counter Values:\n";
343 for (uint64_t Count
: Func
.Counts
)
346 uint32_t NumValueKinds
= Func
.getNumValueKinds();
347 if (!NumValueKinds
) {
352 OS
<< "# Num Value Kinds:\n" << Func
.getNumValueKinds() << "\n";
353 for (uint32_t VK
= 0; VK
< IPVK_Last
+ 1; VK
++) {
354 uint32_t NS
= Func
.getNumValueSites(VK
);
357 OS
<< "# ValueKind = " << ValueProfKindStr
[VK
] << ":\n" << VK
<< "\n";
358 OS
<< "# NumValueSites:\n" << NS
<< "\n";
359 for (uint32_t S
= 0; S
< NS
; S
++) {
360 uint32_t ND
= Func
.getNumValueDataForSite(VK
, S
);
362 std::unique_ptr
<InstrProfValueData
[]> VD
= Func
.getValueForSite(VK
, S
);
363 for (uint32_t I
= 0; I
< ND
; I
++) {
364 if (VK
== IPVK_IndirectCallTarget
)
365 OS
<< Symtab
.getFuncNameOrExternalSymbol(VD
[I
].Value
) << ":"
366 << VD
[I
].Count
<< "\n";
368 OS
<< VD
[I
].Value
<< ":" << VD
[I
].Count
<< "\n";
376 Error
InstrProfWriter::writeText(raw_fd_ostream
&OS
) {
377 if (ProfileKind
== PF_IRLevel
)
378 OS
<< "# IR level Instrumentation Flag\n:ir\n";
379 InstrProfSymtab Symtab
;
380 for (const auto &I
: FunctionData
)
381 if (shouldEncodeData(I
.getValue()))
382 if (Error E
= Symtab
.addFuncName(I
.getKey()))
385 for (const auto &I
: FunctionData
)
386 if (shouldEncodeData(I
.getValue()))
387 for (const auto &Func
: I
.getValue())
388 writeRecordInText(I
.getKey(), Func
.first
, Func
.second
, Symtab
, OS
);
389 return Error::success();