1 //===- InstrProfWriter.cpp - Instrumented profiling writer ----------------===// 2 // 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 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file contains support for writing profiling data for clang's 10 // instrumentation based PGO and coverage. 11 // 12 //===----------------------------------------------------------------------===// 13 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/DataAccessProf.h" 20 #include "llvm/ProfileData/IndexedMemProfData.h" 21 #include "llvm/ProfileData/InstrProf.h" 22 #include "llvm/ProfileData/ProfileCommon.h" 23 #include "llvm/Support/Compression.h" 24 #include "llvm/Support/EndianStream.h" 25 #include "llvm/Support/Error.h" 26 #include "llvm/Support/MemoryBuffer.h" 27 #include "llvm/Support/OnDiskHashTable.h" 28 #include "llvm/Support/raw_ostream.h" 29 #include <cstdint> 30 #include <ctime> 31 #include <memory> 32 #include <string> 33 #include <tuple> 34 #include <utility> 35 #include <vector> 36 37 using namespace llvm; 38 39 namespace llvm { 40 41 class InstrProfRecordWriterTrait { 42 public: 43 using key_type = StringRef; 44 using key_type_ref = StringRef; 45 46 using data_type = const InstrProfWriter::ProfilingData *const; 47 using data_type_ref = const InstrProfWriter::ProfilingData *const; 48 49 using hash_value_type = uint64_t; 50 using offset_type = uint64_t; 51 52 llvm::endianness ValueProfDataEndianness = llvm::endianness::little; 53 InstrProfSummaryBuilder *SummaryBuilder; 54 InstrProfSummaryBuilder *CSSummaryBuilder; 55 56 InstrProfRecordWriterTrait() = default; 57 58 static hash_value_type ComputeHash(key_type_ref K) { 59 return IndexedInstrProf::ComputeHash(K); 60 } 61 62 static std::pair<offset_type, offset_type> 63 EmitKeyDataLength(raw_ostream &Out, key_type_ref K, data_type_ref V) { 64 using namespace support; 65 66 endian::Writer LE(Out, llvm::endianness::little); 67 68 offset_type N = K.size(); 69 LE.write<offset_type>(N); 70 71 offset_type M = 0; 72 for (const auto &ProfileData : *V) { 73 const InstrProfRecord &ProfRecord = ProfileData.second; 74 M += sizeof(uint64_t); // The function hash 75 M += sizeof(uint64_t); // The size of the Counts vector 76 M += ProfRecord.Counts.size() * sizeof(uint64_t); 77 M += sizeof(uint64_t); // The size of the Bitmap vector 78 M += ProfRecord.BitmapBytes.size() * sizeof(uint64_t); 79 80 // Value data 81 M += ValueProfData::getSize(ProfileData.second); 82 } 83 LE.write<offset_type>(M); 84 85 return std::make_pair(N, M); 86 } 87 88 void EmitKey(raw_ostream &Out, key_type_ref K, offset_type N) { 89 Out.write(K.data(), N); 90 } 91 92 void EmitData(raw_ostream &Out, key_type_ref, data_type_ref V, offset_type) { 93 using namespace support; 94 95 endian::Writer LE(Out, llvm::endianness::little); 96 for (const auto &ProfileData : *V) { 97 const InstrProfRecord &ProfRecord = ProfileData.second; 98 if (NamedInstrProfRecord::hasCSFlagInHash(ProfileData.first)) 99 CSSummaryBuilder->addRecord(ProfRecord); 100 else 101 SummaryBuilder->addRecord(ProfRecord); 102 103 LE.write<uint64_t>(ProfileData.first); // Function hash 104 LE.write<uint64_t>(ProfRecord.Counts.size()); 105 for (uint64_t I : ProfRecord.Counts) 106 LE.write<uint64_t>(I); 107 108 LE.write<uint64_t>(ProfRecord.BitmapBytes.size()); 109 for (uint64_t I : ProfRecord.BitmapBytes) 110 LE.write<uint64_t>(I); 111 112 // Write value data 113 std::unique_ptr<ValueProfData> VDataPtr = 114 ValueProfData::serializeFrom(ProfileData.second); 115 uint32_t S = VDataPtr->getSize(); 116 VDataPtr->swapBytesFromHost(ValueProfDataEndianness); 117 Out.write((const char *)VDataPtr.get(), S); 118 } 119 } 120 }; 121 122 } // end namespace llvm 123 124 InstrProfWriter::InstrProfWriter( 125 bool Sparse, uint64_t TemporalProfTraceReservoirSize, 126 uint64_t MaxTemporalProfTraceLength, bool WritePrevVersion, 127 memprof::IndexedVersion MemProfVersionRequested, bool MemProfFullSchema, 128 bool MemprofGenerateRandomHotness, 129 unsigned MemprofGenerateRandomHotnessSeed) 130 : Sparse(Sparse), MaxTemporalProfTraceLength(MaxTemporalProfTraceLength), 131 TemporalProfTraceReservoirSize(TemporalProfTraceReservoirSize), 132 InfoObj(new InstrProfRecordWriterTrait()), 133 WritePrevVersion(WritePrevVersion), 134 MemProfVersionRequested(MemProfVersionRequested), 135 MemProfFullSchema(MemProfFullSchema), 136 MemprofGenerateRandomHotness(MemprofGenerateRandomHotness) { 137 // Set up the random number seed if requested. 138 if (MemprofGenerateRandomHotness) { 139 unsigned seed = MemprofGenerateRandomHotnessSeed 140 ? MemprofGenerateRandomHotnessSeed 141 : std::time(nullptr); 142 errs() << "random hotness seed = " << seed << "\n"; 143 std::srand(seed); 144 } 145 } 146 147 InstrProfWriter::~InstrProfWriter() { delete InfoObj; } 148 149 // Internal interface for testing purpose only. 150 void InstrProfWriter::setValueProfDataEndianness(llvm::endianness Endianness) { 151 InfoObj->ValueProfDataEndianness = Endianness; 152 } 153 154 void InstrProfWriter::setOutputSparse(bool Sparse) { this->Sparse = Sparse; } 155 156 void InstrProfWriter::addRecord(NamedInstrProfRecord &&I, uint64_t Weight, 157 function_ref<void(Error)> Warn) { 158 auto Name = I.Name; 159 auto Hash = I.Hash; 160 addRecord(Name, Hash, std::move(I), Weight, Warn); 161 } 162 163 void InstrProfWriter::overlapRecord(NamedInstrProfRecord &&Other, 164 OverlapStats &Overlap, 165 OverlapStats &FuncLevelOverlap, 166 const OverlapFuncFilters &FuncFilter) { 167 auto Name = Other.Name; 168 auto Hash = Other.Hash; 169 Other.accumulateCounts(FuncLevelOverlap.Test); 170 auto It = FunctionData.find(Name); 171 if (It == FunctionData.end()) { 172 Overlap.addOneUnique(FuncLevelOverlap.Test); 173 return; 174 } 175 if (FuncLevelOverlap.Test.CountSum < 1.0f) { 176 Overlap.Overlap.NumEntries += 1; 177 return; 178 } 179 auto &ProfileDataMap = It->second; 180 auto [Where, NewFunc] = ProfileDataMap.try_emplace(Hash); 181 if (NewFunc) { 182 Overlap.addOneMismatch(FuncLevelOverlap.Test); 183 return; 184 } 185 InstrProfRecord &Dest = Where->second; 186 187 uint64_t ValueCutoff = FuncFilter.ValueCutoff; 188 if (!FuncFilter.NameFilter.empty() && Name.contains(FuncFilter.NameFilter)) 189 ValueCutoff = 0; 190 191 Dest.overlap(Other, Overlap, FuncLevelOverlap, ValueCutoff); 192 } 193 194 void InstrProfWriter::addRecord(StringRef Name, uint64_t Hash, 195 InstrProfRecord &&I, uint64_t Weight, 196 function_ref<void(Error)> Warn) { 197 auto &ProfileDataMap = FunctionData[Name]; 198 199 auto [Where, NewFunc] = ProfileDataMap.try_emplace(Hash); 200 InstrProfRecord &Dest = Where->second; 201 202 auto MapWarn = [&](instrprof_error E) { 203 Warn(make_error<InstrProfError>(E)); 204 }; 205 206 if (NewFunc) { 207 // We've never seen a function with this name and hash, add it. 208 Dest = std::move(I); 209 if (Weight > 1) 210 Dest.scale(Weight, 1, MapWarn); 211 } else { 212 // We're updating a function we've seen before. 213 Dest.merge(I, Weight, MapWarn); 214 } 215 216 Dest.sortValueData(); 217 } 218 219 void InstrProfWriter::addMemProfRecord( 220 const Function::GUID Id, const memprof::IndexedMemProfRecord &Record) { 221 auto NewRecord = Record; 222 // Provoke random hotness values if requested. We specify the lifetime access 223 // density and lifetime length that will result in a cold or not cold hotness. 224 // See the logic in getAllocType() in Analysis/MemoryProfileInfo.cpp. 225 if (MemprofGenerateRandomHotness) { 226 for (auto &Alloc : NewRecord.AllocSites) { 227 // To get a not cold context, set the lifetime access density to the 228 // maximum value and the lifetime to 0. 229 uint64_t NewTLAD = std::numeric_limits<uint64_t>::max(); 230 uint64_t NewTL = 0; 231 bool IsCold = std::rand() % 2; 232 if (IsCold) { 233 // To get a cold context, set the lifetime access density to 0 and the 234 // lifetime to the maximum value. 235 NewTLAD = 0; 236 NewTL = std::numeric_limits<uint64_t>::max(); 237 } 238 Alloc.Info.setTotalLifetimeAccessDensity(NewTLAD); 239 Alloc.Info.setTotalLifetime(NewTL); 240 } 241 } 242 MemProfSumBuilder.addRecord(NewRecord); 243 auto [Iter, Inserted] = MemProfData.Records.insert({Id, NewRecord}); 244 // If we inserted a new record then we are done. 245 if (Inserted) { 246 return; 247 } 248 memprof::IndexedMemProfRecord &Existing = Iter->second; 249 Existing.merge(NewRecord); 250 } 251 252 bool InstrProfWriter::addMemProfFrame(const memprof::FrameId Id, 253 const memprof::Frame &Frame, 254 function_ref<void(Error)> Warn) { 255 auto [Iter, Inserted] = MemProfData.Frames.insert({Id, Frame}); 256 // If a mapping already exists for the current frame id and it does not 257 // match the new mapping provided then reset the existing contents and bail 258 // out. We don't support the merging of memprof data whose Frame -> Id 259 // mapping across profiles is inconsistent. 260 if (!Inserted && Iter->second != Frame) { 261 Warn(make_error<InstrProfError>(instrprof_error::malformed, 262 "frame to id mapping mismatch")); 263 return false; 264 } 265 return true; 266 } 267 268 bool InstrProfWriter::addMemProfCallStack( 269 const memprof::CallStackId CSId, 270 const llvm::SmallVector<memprof::FrameId> &CallStack, 271 function_ref<void(Error)> Warn) { 272 auto [Iter, Inserted] = MemProfData.CallStacks.insert({CSId, CallStack}); 273 // If a mapping already exists for the current call stack id and it does not 274 // match the new mapping provided then reset the existing contents and bail 275 // out. We don't support the merging of memprof data whose CallStack -> Id 276 // mapping across profiles is inconsistent. 277 if (!Inserted && Iter->second != CallStack) { 278 Warn(make_error<InstrProfError>(instrprof_error::malformed, 279 "call stack to id mapping mismatch")); 280 return false; 281 } 282 return true; 283 } 284 285 bool InstrProfWriter::addMemProfData(memprof::IndexedMemProfData Incoming, 286 function_ref<void(Error)> Warn) { 287 // Return immediately if everything is empty. 288 if (Incoming.Frames.empty() && Incoming.CallStacks.empty() && 289 Incoming.Records.empty()) 290 return true; 291 292 // Otherwise, every component must be non-empty. 293 assert(!Incoming.Frames.empty() && !Incoming.CallStacks.empty() && 294 !Incoming.Records.empty()); 295 296 if (MemProfData.Frames.empty()) 297 MemProfData.Frames = std::move(Incoming.Frames); 298 else 299 for (const auto &[Id, F] : Incoming.Frames) 300 if (addMemProfFrame(Id, F, Warn)) 301 return false; 302 303 if (MemProfData.CallStacks.empty()) 304 MemProfData.CallStacks = std::move(Incoming.CallStacks); 305 else 306 for (const auto &[CSId, CS] : Incoming.CallStacks) 307 if (addMemProfCallStack(CSId, CS, Warn)) 308 return false; 309 310 // Add one record at a time if randomization is requested. 311 if (MemProfData.Records.empty() && !MemprofGenerateRandomHotness) { 312 // Need to manually add each record to the builder, which is otherwise done 313 // in addMemProfRecord. 314 for (const auto &[GUID, Record] : Incoming.Records) 315 MemProfSumBuilder.addRecord(Record); 316 MemProfData.Records = std::move(Incoming.Records); 317 } else { 318 for (const auto &[GUID, Record] : Incoming.Records) 319 addMemProfRecord(GUID, Record); 320 } 321 322 return true; 323 } 324 325 void InstrProfWriter::addBinaryIds(ArrayRef<llvm::object::BuildID> BIs) { 326 llvm::append_range(BinaryIds, BIs); 327 } 328 329 void InstrProfWriter::addDataAccessProfData( 330 std::unique_ptr<memprof::DataAccessProfData> DataAccessProfDataIn) { 331 DataAccessProfileData = std::move(DataAccessProfDataIn); 332 } 333 334 void InstrProfWriter::addTemporalProfileTrace(TemporalProfTraceTy Trace) { 335 assert(Trace.FunctionNameRefs.size() <= MaxTemporalProfTraceLength); 336 assert(!Trace.FunctionNameRefs.empty()); 337 if (TemporalProfTraceStreamSize < TemporalProfTraceReservoirSize) { 338 // Simply append the trace if we have not yet hit our reservoir size limit. 339 TemporalProfTraces.push_back(std::move(Trace)); 340 } else { 341 // Otherwise, replace a random trace in the stream. 342 std::uniform_int_distribution<uint64_t> Distribution( 343 0, TemporalProfTraceStreamSize); 344 uint64_t RandomIndex = Distribution(RNG); 345 if (RandomIndex < TemporalProfTraces.size()) 346 TemporalProfTraces[RandomIndex] = std::move(Trace); 347 } 348 ++TemporalProfTraceStreamSize; 349 } 350 351 void InstrProfWriter::addTemporalProfileTraces( 352 SmallVectorImpl<TemporalProfTraceTy> &SrcTraces, uint64_t SrcStreamSize) { 353 for (auto &Trace : SrcTraces) 354 if (Trace.FunctionNameRefs.size() > MaxTemporalProfTraceLength) 355 Trace.FunctionNameRefs.resize(MaxTemporalProfTraceLength); 356 llvm::erase_if(SrcTraces, [](auto &T) { return T.FunctionNameRefs.empty(); }); 357 // Assume that the source has the same reservoir size as the destination to 358 // avoid needing to record it in the indexed profile format. 359 bool IsDestSampled = 360 (TemporalProfTraceStreamSize > TemporalProfTraceReservoirSize); 361 bool IsSrcSampled = (SrcStreamSize > TemporalProfTraceReservoirSize); 362 if (!IsDestSampled && IsSrcSampled) { 363 // If one of the traces are sampled, ensure that it belongs to Dest. 364 std::swap(TemporalProfTraces, SrcTraces); 365 std::swap(TemporalProfTraceStreamSize, SrcStreamSize); 366 std::swap(IsDestSampled, IsSrcSampled); 367 } 368 if (!IsSrcSampled) { 369 // If the source stream is not sampled, we add each source trace normally. 370 for (auto &Trace : SrcTraces) 371 addTemporalProfileTrace(std::move(Trace)); 372 return; 373 } 374 // Otherwise, we find the traces that would have been removed if we added 375 // the whole source stream. 376 SmallSetVector<uint64_t, 8> IndicesToReplace; 377 for (uint64_t I = 0; I < SrcStreamSize; I++) { 378 std::uniform_int_distribution<uint64_t> Distribution( 379 0, TemporalProfTraceStreamSize); 380 uint64_t RandomIndex = Distribution(RNG); 381 if (RandomIndex < TemporalProfTraces.size()) 382 IndicesToReplace.insert(RandomIndex); 383 ++TemporalProfTraceStreamSize; 384 } 385 // Then we insert a random sample of the source traces. 386 llvm::shuffle(SrcTraces.begin(), SrcTraces.end(), RNG); 387 for (const auto &[Index, Trace] : llvm::zip(IndicesToReplace, SrcTraces)) 388 TemporalProfTraces[Index] = std::move(Trace); 389 } 390 391 void InstrProfWriter::mergeRecordsFromWriter(InstrProfWriter &&IPW, 392 function_ref<void(Error)> Warn) { 393 for (auto &I : IPW.FunctionData) 394 for (auto &Func : I.getValue()) 395 addRecord(I.getKey(), Func.first, std::move(Func.second), 1, Warn); 396 397 BinaryIds.reserve(BinaryIds.size() + IPW.BinaryIds.size()); 398 for (auto &I : IPW.BinaryIds) 399 addBinaryIds(I); 400 401 addTemporalProfileTraces(IPW.TemporalProfTraces, 402 IPW.TemporalProfTraceStreamSize); 403 404 MemProfData.Frames.reserve(IPW.MemProfData.Frames.size()); 405 for (auto &[FrameId, Frame] : IPW.MemProfData.Frames) { 406 // If we weren't able to add the frame mappings then it doesn't make sense 407 // to try to merge the records from this profile. 408 if (!addMemProfFrame(FrameId, Frame, Warn)) 409 return; 410 } 411 412 MemProfData.CallStacks.reserve(IPW.MemProfData.CallStacks.size()); 413 for (auto &[CSId, CallStack] : IPW.MemProfData.CallStacks) { 414 if (!addMemProfCallStack(CSId, CallStack, Warn)) 415 return; 416 } 417 418 MemProfData.Records.reserve(IPW.MemProfData.Records.size()); 419 for (auto &[GUID, Record] : IPW.MemProfData.Records) { 420 addMemProfRecord(GUID, Record); 421 } 422 } 423 424 bool InstrProfWriter::shouldEncodeData(const ProfilingData &PD) { 425 if (!Sparse) 426 return true; 427 for (const auto &Func : PD) { 428 const InstrProfRecord &IPR = Func.second; 429 if (llvm::any_of(IPR.Counts, [](uint64_t Count) { return Count > 0; })) 430 return true; 431 if (llvm::any_of(IPR.BitmapBytes, [](uint8_t Byte) { return Byte > 0; })) 432 return true; 433 } 434 return false; 435 } 436 437 static void setSummary(IndexedInstrProf::Summary *TheSummary, 438 ProfileSummary &PS) { 439 using namespace IndexedInstrProf; 440 441 const std::vector<ProfileSummaryEntry> &Res = PS.getDetailedSummary(); 442 TheSummary->NumSummaryFields = Summary::NumKinds; 443 TheSummary->NumCutoffEntries = Res.size(); 444 TheSummary->set(Summary::MaxFunctionCount, PS.getMaxFunctionCount()); 445 TheSummary->set(Summary::MaxBlockCount, PS.getMaxCount()); 446 TheSummary->set(Summary::MaxInternalBlockCount, PS.getMaxInternalCount()); 447 TheSummary->set(Summary::TotalBlockCount, PS.getTotalCount()); 448 TheSummary->set(Summary::TotalNumBlocks, PS.getNumCounts()); 449 TheSummary->set(Summary::TotalNumFunctions, PS.getNumFunctions()); 450 for (unsigned I = 0; I < Res.size(); I++) 451 TheSummary->setEntry(I, Res[I]); 452 } 453 454 uint64_t InstrProfWriter::writeHeader(const IndexedInstrProf::Header &Header, 455 const bool WritePrevVersion, 456 ProfOStream &OS) { 457 // Only write out the first four fields. 458 for (int I = 0; I < 4; I++) 459 OS.write(reinterpret_cast<const uint64_t *>(&Header)[I]); 460 461 // Remember the offset of the remaining fields to allow back patching later. 462 auto BackPatchStartOffset = OS.tell(); 463 464 // Reserve the space for back patching later. 465 OS.write(0); // HashOffset 466 OS.write(0); // MemProfOffset 467 OS.write(0); // BinaryIdOffset 468 OS.write(0); // TemporalProfTracesOffset 469 if (!WritePrevVersion) 470 OS.write(0); // VTableNamesOffset 471 472 return BackPatchStartOffset; 473 } 474 475 Error InstrProfWriter::writeBinaryIds(ProfOStream &OS) { 476 // BinaryIdSection has two parts: 477 // 1. uint64_t BinaryIdsSectionSize 478 // 2. list of binary ids that consist of: 479 // a. uint64_t BinaryIdLength 480 // b. uint8_t BinaryIdData 481 // c. uint8_t Padding (if necessary) 482 // Calculate size of binary section. 483 uint64_t BinaryIdsSectionSize = 0; 484 485 // Remove duplicate binary ids. 486 llvm::sort(BinaryIds); 487 BinaryIds.erase(llvm::unique(BinaryIds), BinaryIds.end()); 488 489 for (const auto &BI : BinaryIds) { 490 // Increment by binary id length data type size. 491 BinaryIdsSectionSize += sizeof(uint64_t); 492 // Increment by binary id data length, aligned to 8 bytes. 493 BinaryIdsSectionSize += alignToPowerOf2(BI.size(), sizeof(uint64_t)); 494 } 495 // Write binary ids section size. 496 OS.write(BinaryIdsSectionSize); 497 498 for (const auto &BI : BinaryIds) { 499 uint64_t BILen = BI.size(); 500 // Write binary id length. 501 OS.write(BILen); 502 // Write binary id data. 503 for (unsigned K = 0; K < BILen; K++) 504 OS.writeByte(BI[K]); 505 // Write padding if necessary. 506 uint64_t PaddingSize = alignToPowerOf2(BILen, sizeof(uint64_t)) - BILen; 507 for (unsigned K = 0; K < PaddingSize; K++) 508 OS.writeByte(0); 509 } 510 511 return Error::success(); 512 } 513 514 Error InstrProfWriter::writeVTableNames(ProfOStream &OS) { 515 std::vector<std::string> VTableNameStrs; 516 for (StringRef VTableName : VTableNames.keys()) 517 VTableNameStrs.push_back(VTableName.str()); 518 519 std::string CompressedVTableNames; 520 if (!VTableNameStrs.empty()) 521 if (Error E = collectGlobalObjectNameStrings( 522 VTableNameStrs, compression::zlib::isAvailable(), 523 CompressedVTableNames)) 524 return E; 525 526 const uint64_t CompressedStringLen = CompressedVTableNames.length(); 527 528 // Record the length of compressed string. 529 OS.write(CompressedStringLen); 530 531 // Write the chars in compressed strings. 532 for (auto &c : CompressedVTableNames) 533 OS.writeByte(static_cast<uint8_t>(c)); 534 535 // Pad up to a multiple of 8. 536 // InstrProfReader could read bytes according to 'CompressedStringLen'. 537 const uint64_t PaddedLength = alignTo(CompressedStringLen, 8); 538 539 for (uint64_t K = CompressedStringLen; K < PaddedLength; K++) 540 OS.writeByte(0); 541 542 return Error::success(); 543 } 544 545 Error InstrProfWriter::writeImpl(ProfOStream &OS) { 546 using namespace IndexedInstrProf; 547 using namespace support; 548 549 OnDiskChainedHashTableGenerator<InstrProfRecordWriterTrait> Generator; 550 551 InstrProfSummaryBuilder ISB(ProfileSummaryBuilder::DefaultCutoffs); 552 InfoObj->SummaryBuilder = &ISB; 553 InstrProfSummaryBuilder CSISB(ProfileSummaryBuilder::DefaultCutoffs); 554 InfoObj->CSSummaryBuilder = &CSISB; 555 556 // Populate the hash table generator. 557 SmallVector<std::pair<StringRef, const ProfilingData *>> OrderedData; 558 for (const auto &I : FunctionData) 559 if (shouldEncodeData(I.getValue())) 560 OrderedData.emplace_back((I.getKey()), &I.getValue()); 561 llvm::sort(OrderedData, less_first()); 562 for (const auto &I : OrderedData) 563 Generator.insert(I.first, I.second); 564 565 // Write the header. 566 IndexedInstrProf::Header Header; 567 Header.Version = WritePrevVersion 568 ? IndexedInstrProf::ProfVersion::Version11 569 : IndexedInstrProf::ProfVersion::CurrentVersion; 570 // The WritePrevVersion handling will either need to be removed or updated 571 // if the version is advanced beyond 12. 572 static_assert(IndexedInstrProf::ProfVersion::CurrentVersion == 573 IndexedInstrProf::ProfVersion::Version12); 574 if (static_cast<bool>(ProfileKind & InstrProfKind::IRInstrumentation)) 575 Header.Version |= VARIANT_MASK_IR_PROF; 576 if (static_cast<bool>(ProfileKind & InstrProfKind::ContextSensitive)) 577 Header.Version |= VARIANT_MASK_CSIR_PROF; 578 if (static_cast<bool>(ProfileKind & 579 InstrProfKind::FunctionEntryInstrumentation)) 580 Header.Version |= VARIANT_MASK_INSTR_ENTRY; 581 if (static_cast<bool>(ProfileKind & 582 InstrProfKind::LoopEntriesInstrumentation)) 583 Header.Version |= VARIANT_MASK_INSTR_LOOP_ENTRIES; 584 if (static_cast<bool>(ProfileKind & InstrProfKind::SingleByteCoverage)) 585 Header.Version |= VARIANT_MASK_BYTE_COVERAGE; 586 if (static_cast<bool>(ProfileKind & InstrProfKind::FunctionEntryOnly)) 587 Header.Version |= VARIANT_MASK_FUNCTION_ENTRY_ONLY; 588 if (static_cast<bool>(ProfileKind & InstrProfKind::MemProf)) 589 Header.Version |= VARIANT_MASK_MEMPROF; 590 if (static_cast<bool>(ProfileKind & InstrProfKind::TemporalProfile)) 591 Header.Version |= VARIANT_MASK_TEMPORAL_PROF; 592 593 const uint64_t BackPatchStartOffset = 594 writeHeader(Header, WritePrevVersion, OS); 595 596 // Reserve space to write profile summary data. 597 uint32_t NumEntries = ProfileSummaryBuilder::DefaultCutoffs.size(); 598 uint32_t SummarySize = Summary::getSize(Summary::NumKinds, NumEntries); 599 // Remember the summary offset. 600 uint64_t SummaryOffset = OS.tell(); 601 for (unsigned I = 0; I < SummarySize / sizeof(uint64_t); I++) 602 OS.write(0); 603 uint64_t CSSummaryOffset = 0; 604 uint64_t CSSummarySize = 0; 605 if (static_cast<bool>(ProfileKind & InstrProfKind::ContextSensitive)) { 606 CSSummaryOffset = OS.tell(); 607 CSSummarySize = SummarySize / sizeof(uint64_t); 608 for (unsigned I = 0; I < CSSummarySize; I++) 609 OS.write(0); 610 } 611 612 // Write the hash table. 613 uint64_t HashTableStart = Generator.Emit(OS.OS, *InfoObj); 614 615 // Write the MemProf profile data if we have it. 616 uint64_t MemProfSectionStart = 0; 617 if (static_cast<bool>(ProfileKind & InstrProfKind::MemProf)) { 618 MemProfSectionStart = OS.tell(); 619 620 if (auto E = writeMemProf( 621 OS, MemProfData, MemProfVersionRequested, MemProfFullSchema, 622 std::move(DataAccessProfileData), MemProfSumBuilder.getSummary())) 623 return E; 624 } 625 626 uint64_t BinaryIdSectionStart = OS.tell(); 627 if (auto E = writeBinaryIds(OS)) 628 return E; 629 630 uint64_t VTableNamesSectionStart = OS.tell(); 631 632 if (!WritePrevVersion) 633 if (Error E = writeVTableNames(OS)) 634 return E; 635 636 uint64_t TemporalProfTracesSectionStart = 0; 637 if (static_cast<bool>(ProfileKind & InstrProfKind::TemporalProfile)) { 638 TemporalProfTracesSectionStart = OS.tell(); 639 OS.write(TemporalProfTraces.size()); 640 OS.write(TemporalProfTraceStreamSize); 641 for (auto &Trace : TemporalProfTraces) { 642 OS.write(Trace.Weight); 643 OS.write(Trace.FunctionNameRefs.size()); 644 for (auto &NameRef : Trace.FunctionNameRefs) 645 OS.write(NameRef); 646 } 647 } 648 649 // Allocate space for data to be serialized out. 650 std::unique_ptr<IndexedInstrProf::Summary> TheSummary = 651 IndexedInstrProf::allocSummary(SummarySize); 652 // Compute the Summary and copy the data to the data 653 // structure to be serialized out (to disk or buffer). 654 std::unique_ptr<ProfileSummary> PS = ISB.getSummary(); 655 setSummary(TheSummary.get(), *PS); 656 InfoObj->SummaryBuilder = nullptr; 657 658 // For Context Sensitive summary. 659 std::unique_ptr<IndexedInstrProf::Summary> TheCSSummary = nullptr; 660 if (static_cast<bool>(ProfileKind & InstrProfKind::ContextSensitive)) { 661 TheCSSummary = IndexedInstrProf::allocSummary(SummarySize); 662 std::unique_ptr<ProfileSummary> CSPS = CSISB.getSummary(); 663 setSummary(TheCSSummary.get(), *CSPS); 664 } 665 InfoObj->CSSummaryBuilder = nullptr; 666 667 SmallVector<uint64_t, 8> HeaderOffsets = {HashTableStart, MemProfSectionStart, 668 BinaryIdSectionStart, 669 TemporalProfTracesSectionStart}; 670 if (!WritePrevVersion) 671 HeaderOffsets.push_back(VTableNamesSectionStart); 672 673 PatchItem PatchItems[] = { 674 // Patch the Header fields 675 {BackPatchStartOffset, HeaderOffsets}, 676 // Patch the summary data. 677 {SummaryOffset, 678 ArrayRef<uint64_t>(reinterpret_cast<uint64_t *>(TheSummary.get()), 679 SummarySize / sizeof(uint64_t))}, 680 {CSSummaryOffset, 681 ArrayRef<uint64_t>(reinterpret_cast<uint64_t *>(TheCSSummary.get()), 682 CSSummarySize)}}; 683 684 OS.patch(PatchItems); 685 686 for (const auto &I : FunctionData) 687 for (const auto &F : I.getValue()) 688 if (Error E = validateRecord(F.second)) 689 return E; 690 691 return Error::success(); 692 } 693 694 Error InstrProfWriter::write(raw_fd_ostream &OS) { 695 // Write the hash table. 696 ProfOStream POS(OS); 697 return writeImpl(POS); 698 } 699 700 Error InstrProfWriter::write(raw_string_ostream &OS) { 701 ProfOStream POS(OS); 702 return writeImpl(POS); 703 } 704 705 std::unique_ptr<MemoryBuffer> InstrProfWriter::writeBuffer() { 706 std::string Data; 707 raw_string_ostream OS(Data); 708 // Write the hash table. 709 if (Error E = write(OS)) 710 return nullptr; 711 // Return this in an aligned memory buffer. 712 return MemoryBuffer::getMemBufferCopy(Data); 713 } 714 715 static const char *ValueProfKindStr[] = { 716 #define VALUE_PROF_KIND(Enumerator, Value, Descr) #Enumerator, 717 #include "llvm/ProfileData/InstrProfData.inc" 718 }; 719 720 Error InstrProfWriter::validateRecord(const InstrProfRecord &Func) { 721 for (uint32_t VK = 0; VK <= IPVK_Last; VK++) { 722 if (VK == IPVK_IndirectCallTarget || VK == IPVK_VTableTarget) 723 continue; 724 uint32_t NS = Func.getNumValueSites(VK); 725 for (uint32_t S = 0; S < NS; S++) { 726 DenseSet<uint64_t> SeenValues; 727 for (const auto &V : Func.getValueArrayForSite(VK, S)) 728 if (!SeenValues.insert(V.Value).second) 729 return make_error<InstrProfError>(instrprof_error::invalid_prof); 730 } 731 } 732 733 return Error::success(); 734 } 735 736 void InstrProfWriter::writeRecordInText(StringRef Name, uint64_t Hash, 737 const InstrProfRecord &Func, 738 InstrProfSymtab &Symtab, 739 raw_fd_ostream &OS) { 740 OS << Name << "\n"; 741 OS << "# Func Hash:\n" << Hash << "\n"; 742 OS << "# Num Counters:\n" << Func.Counts.size() << "\n"; 743 OS << "# Counter Values:\n"; 744 for (uint64_t Count : Func.Counts) 745 OS << Count << "\n"; 746 747 if (Func.BitmapBytes.size() > 0) { 748 OS << "# Num Bitmap Bytes:\n$" << Func.BitmapBytes.size() << "\n"; 749 OS << "# Bitmap Byte Values:\n"; 750 for (uint8_t Byte : Func.BitmapBytes) { 751 OS << "0x"; 752 OS.write_hex(Byte); 753 OS << "\n"; 754 } 755 OS << "\n"; 756 } 757 758 uint32_t NumValueKinds = Func.getNumValueKinds(); 759 if (!NumValueKinds) { 760 OS << "\n"; 761 return; 762 } 763 764 OS << "# Num Value Kinds:\n" << Func.getNumValueKinds() << "\n"; 765 for (uint32_t VK = 0; VK < IPVK_Last + 1; VK++) { 766 uint32_t NS = Func.getNumValueSites(VK); 767 if (!NS) 768 continue; 769 OS << "# ValueKind = " << ValueProfKindStr[VK] << ":\n" << VK << "\n"; 770 OS << "# NumValueSites:\n" << NS << "\n"; 771 for (uint32_t S = 0; S < NS; S++) { 772 auto VD = Func.getValueArrayForSite(VK, S); 773 OS << VD.size() << "\n"; 774 for (const auto &V : VD) { 775 if (VK == IPVK_IndirectCallTarget || VK == IPVK_VTableTarget) 776 OS << Symtab.getFuncOrVarNameIfDefined(V.Value) << ":" << V.Count 777 << "\n"; 778 else 779 OS << V.Value << ":" << V.Count << "\n"; 780 } 781 } 782 } 783 784 OS << "\n"; 785 } 786 787 Error InstrProfWriter::writeText(raw_fd_ostream &OS) { 788 // Check CS first since it implies an IR level profile. 789 if (static_cast<bool>(ProfileKind & InstrProfKind::ContextSensitive)) 790 OS << "# CSIR level Instrumentation Flag\n:csir\n"; 791 else if (static_cast<bool>(ProfileKind & InstrProfKind::IRInstrumentation)) 792 OS << "# IR level Instrumentation Flag\n:ir\n"; 793 794 if (static_cast<bool>(ProfileKind & 795 InstrProfKind::FunctionEntryInstrumentation)) 796 OS << "# Always instrument the function entry block\n:entry_first\n"; 797 if (static_cast<bool>(ProfileKind & 798 InstrProfKind::LoopEntriesInstrumentation)) 799 OS << "# Always instrument the loop entry " 800 "blocks\n:instrument_loop_entries\n"; 801 if (static_cast<bool>(ProfileKind & InstrProfKind::SingleByteCoverage)) 802 OS << "# Instrument block coverage\n:single_byte_coverage\n"; 803 InstrProfSymtab Symtab; 804 805 using FuncPair = detail::DenseMapPair<uint64_t, InstrProfRecord>; 806 using RecordType = std::pair<StringRef, FuncPair>; 807 SmallVector<RecordType, 4> OrderedFuncData; 808 809 for (const auto &I : FunctionData) { 810 if (shouldEncodeData(I.getValue())) { 811 if (Error E = Symtab.addFuncName(I.getKey())) 812 return E; 813 for (const auto &Func : I.getValue()) 814 OrderedFuncData.push_back(std::make_pair(I.getKey(), Func)); 815 } 816 } 817 818 for (const auto &VTableName : VTableNames) 819 if (Error E = Symtab.addVTableName(VTableName.getKey())) 820 return E; 821 822 if (static_cast<bool>(ProfileKind & InstrProfKind::TemporalProfile)) 823 writeTextTemporalProfTraceData(OS, Symtab); 824 825 llvm::sort(OrderedFuncData, [](const RecordType &A, const RecordType &B) { 826 return std::tie(A.first, A.second.first) < 827 std::tie(B.first, B.second.first); 828 }); 829 830 for (const auto &record : OrderedFuncData) { 831 const StringRef &Name = record.first; 832 const FuncPair &Func = record.second; 833 writeRecordInText(Name, Func.first, Func.second, Symtab, OS); 834 } 835 836 for (const auto &record : OrderedFuncData) { 837 const FuncPair &Func = record.second; 838 if (Error E = validateRecord(Func.second)) 839 return E; 840 } 841 842 return Error::success(); 843 } 844 845 void InstrProfWriter::writeTextTemporalProfTraceData(raw_fd_ostream &OS, 846 InstrProfSymtab &Symtab) { 847 OS << ":temporal_prof_traces\n"; 848 OS << "# Num Temporal Profile Traces:\n" << TemporalProfTraces.size() << "\n"; 849 OS << "# Temporal Profile Trace Stream Size:\n" 850 << TemporalProfTraceStreamSize << "\n"; 851 for (auto &Trace : TemporalProfTraces) { 852 OS << "# Weight:\n" << Trace.Weight << "\n"; 853 for (auto &NameRef : Trace.FunctionNameRefs) 854 OS << Symtab.getFuncOrVarName(NameRef) << ","; 855 OS << "\n"; 856 } 857 OS << "\n"; 858 } 859