1 //===- llvm-profdata.cpp - LLVM profile data tool -------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // llvm-profdata merges .profdata files. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "llvm/ADT/SmallSet.h" 15 #include "llvm/ADT/SmallVector.h" 16 #include "llvm/ADT/StringRef.h" 17 #include "llvm/IR/LLVMContext.h" 18 #include "llvm/ProfileData/InstrProfReader.h" 19 #include "llvm/ProfileData/InstrProfWriter.h" 20 #include "llvm/ProfileData/ProfileCommon.h" 21 #include "llvm/ProfileData/SampleProfReader.h" 22 #include "llvm/ProfileData/SampleProfWriter.h" 23 #include "llvm/Support/CommandLine.h" 24 #include "llvm/Support/Errc.h" 25 #include "llvm/Support/FileSystem.h" 26 #include "llvm/Support/Format.h" 27 #include "llvm/Support/InitLLVM.h" 28 #include "llvm/Support/MemoryBuffer.h" 29 #include "llvm/Support/Path.h" 30 #include "llvm/Support/WithColor.h" 31 #include "llvm/Support/ThreadPool.h" 32 #include "llvm/Support/raw_ostream.h" 33 #include <algorithm> 34 35 using namespace llvm; 36 37 enum ProfileFormat { 38 PF_None = 0, 39 PF_Text, 40 PF_Compact_Binary, 41 PF_GCC, 42 PF_Binary 43 }; 44 45 static void warn(Twine Message, std::string Whence = "", 46 std::string Hint = "") { 47 WithColor::warning(); 48 if (!Whence.empty()) 49 errs() << Whence << ": "; 50 errs() << Message << "\n"; 51 if (!Hint.empty()) 52 WithColor::note() << Hint << "\n"; 53 } 54 55 static void exitWithError(Twine Message, std::string Whence = "", 56 std::string Hint = "") { 57 WithColor::error(); 58 if (!Whence.empty()) 59 errs() << Whence << ": "; 60 errs() << Message << "\n"; 61 if (!Hint.empty()) 62 WithColor::note() << Hint << "\n"; 63 ::exit(1); 64 } 65 66 static void exitWithError(Error E, StringRef Whence = "") { 67 if (E.isA<InstrProfError>()) { 68 handleAllErrors(std::move(E), [&](const InstrProfError &IPE) { 69 instrprof_error instrError = IPE.get(); 70 StringRef Hint = ""; 71 if (instrError == instrprof_error::unrecognized_format) { 72 // Hint for common error of forgetting -sample for sample profiles. 73 Hint = "Perhaps you forgot to use the -sample option?"; 74 } 75 exitWithError(IPE.message(), Whence, Hint); 76 }); 77 } 78 79 exitWithError(toString(std::move(E)), Whence); 80 } 81 82 static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") { 83 exitWithError(EC.message(), Whence); 84 } 85 86 namespace { 87 enum ProfileKinds { instr, sample }; 88 } 89 90 static void handleMergeWriterError(Error E, StringRef WhenceFile = "", 91 StringRef WhenceFunction = "", 92 bool ShowHint = true) { 93 if (!WhenceFile.empty()) 94 errs() << WhenceFile << ": "; 95 if (!WhenceFunction.empty()) 96 errs() << WhenceFunction << ": "; 97 98 auto IPE = instrprof_error::success; 99 E = handleErrors(std::move(E), 100 [&IPE](std::unique_ptr<InstrProfError> E) -> Error { 101 IPE = E->get(); 102 return Error(std::move(E)); 103 }); 104 errs() << toString(std::move(E)) << "\n"; 105 106 if (ShowHint) { 107 StringRef Hint = ""; 108 if (IPE != instrprof_error::success) { 109 switch (IPE) { 110 case instrprof_error::hash_mismatch: 111 case instrprof_error::count_mismatch: 112 case instrprof_error::value_site_count_mismatch: 113 Hint = "Make sure that all profile data to be merged is generated " 114 "from the same binary."; 115 break; 116 default: 117 break; 118 } 119 } 120 121 if (!Hint.empty()) 122 errs() << Hint << "\n"; 123 } 124 } 125 126 struct WeightedFile { 127 std::string Filename; 128 uint64_t Weight; 129 }; 130 typedef SmallVector<WeightedFile, 5> WeightedFileVector; 131 132 /// Keep track of merged data and reported errors. 133 struct WriterContext { 134 std::mutex Lock; 135 InstrProfWriter Writer; 136 Error Err; 137 std::string ErrWhence; 138 std::mutex &ErrLock; 139 SmallSet<instrprof_error, 4> &WriterErrorCodes; 140 141 WriterContext(bool IsSparse, std::mutex &ErrLock, 142 SmallSet<instrprof_error, 4> &WriterErrorCodes) 143 : Lock(), Writer(IsSparse), Err(Error::success()), ErrWhence(""), 144 ErrLock(ErrLock), WriterErrorCodes(WriterErrorCodes) {} 145 }; 146 147 /// Determine whether an error is fatal for profile merging. 148 static bool isFatalError(instrprof_error IPE) { 149 switch (IPE) { 150 default: 151 return true; 152 case instrprof_error::success: 153 case instrprof_error::eof: 154 case instrprof_error::unknown_function: 155 case instrprof_error::hash_mismatch: 156 case instrprof_error::count_mismatch: 157 case instrprof_error::counter_overflow: 158 case instrprof_error::value_site_count_mismatch: 159 return false; 160 } 161 } 162 163 /// Load an input into a writer context. 164 static void loadInput(const WeightedFile &Input, WriterContext *WC) { 165 std::unique_lock<std::mutex> CtxGuard{WC->Lock}; 166 167 // If there's a pending hard error, don't do more work. 168 if (WC->Err) 169 return; 170 171 // Copy the filename, because llvm::ThreadPool copied the input "const 172 // WeightedFile &" by value, making a reference to the filename within it 173 // invalid outside of this packaged task. 174 WC->ErrWhence = Input.Filename; 175 176 auto ReaderOrErr = InstrProfReader::create(Input.Filename); 177 if (Error E = ReaderOrErr.takeError()) { 178 // Skip the empty profiles by returning sliently. 179 instrprof_error IPE = InstrProfError::take(std::move(E)); 180 if (IPE != instrprof_error::empty_raw_profile) 181 WC->Err = make_error<InstrProfError>(IPE); 182 return; 183 } 184 185 auto Reader = std::move(ReaderOrErr.get()); 186 bool IsIRProfile = Reader->isIRLevelProfile(); 187 if (WC->Writer.setIsIRLevelProfile(IsIRProfile)) { 188 WC->Err = make_error<StringError>( 189 "Merge IR generated profile with Clang generated profile.", 190 std::error_code()); 191 return; 192 } 193 194 for (auto &I : *Reader) { 195 const StringRef FuncName = I.Name; 196 bool Reported = false; 197 WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) { 198 if (Reported) { 199 consumeError(std::move(E)); 200 return; 201 } 202 Reported = true; 203 // Only show hint the first time an error occurs. 204 instrprof_error IPE = InstrProfError::take(std::move(E)); 205 std::unique_lock<std::mutex> ErrGuard{WC->ErrLock}; 206 bool firstTime = WC->WriterErrorCodes.insert(IPE).second; 207 handleMergeWriterError(make_error<InstrProfError>(IPE), Input.Filename, 208 FuncName, firstTime); 209 }); 210 } 211 if (Reader->hasError()) { 212 if (Error E = Reader->getError()) { 213 instrprof_error IPE = InstrProfError::take(std::move(E)); 214 if (isFatalError(IPE)) 215 WC->Err = make_error<InstrProfError>(IPE); 216 } 217 } 218 } 219 220 /// Merge the \p Src writer context into \p Dst. 221 static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) { 222 // If we've already seen a hard error, continuing with the merge would 223 // clobber it. 224 if (Dst->Err || Src->Err) 225 return; 226 227 bool Reported = false; 228 Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) { 229 if (Reported) { 230 consumeError(std::move(E)); 231 return; 232 } 233 Reported = true; 234 Dst->Err = std::move(E); 235 }); 236 } 237 238 static void mergeInstrProfile(const WeightedFileVector &Inputs, 239 StringRef OutputFilename, 240 ProfileFormat OutputFormat, bool OutputSparse, 241 unsigned NumThreads) { 242 if (OutputFilename.compare("-") == 0) 243 exitWithError("Cannot write indexed profdata format to stdout."); 244 245 if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary && 246 OutputFormat != PF_Text) 247 exitWithError("Unknown format is specified."); 248 249 std::error_code EC; 250 raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::F_None); 251 if (EC) 252 exitWithErrorCode(EC, OutputFilename); 253 254 std::mutex ErrorLock; 255 SmallSet<instrprof_error, 4> WriterErrorCodes; 256 257 // If NumThreads is not specified, auto-detect a good default. 258 if (NumThreads == 0) 259 NumThreads = 260 std::min(hardware_concurrency(), unsigned((Inputs.size() + 1) / 2)); 261 262 // Initialize the writer contexts. 263 SmallVector<std::unique_ptr<WriterContext>, 4> Contexts; 264 for (unsigned I = 0; I < NumThreads; ++I) 265 Contexts.emplace_back(llvm::make_unique<WriterContext>( 266 OutputSparse, ErrorLock, WriterErrorCodes)); 267 268 if (NumThreads == 1) { 269 for (const auto &Input : Inputs) 270 loadInput(Input, Contexts[0].get()); 271 } else { 272 ThreadPool Pool(NumThreads); 273 274 // Load the inputs in parallel (N/NumThreads serial steps). 275 unsigned Ctx = 0; 276 for (const auto &Input : Inputs) { 277 Pool.async(loadInput, Input, Contexts[Ctx].get()); 278 Ctx = (Ctx + 1) % NumThreads; 279 } 280 Pool.wait(); 281 282 // Merge the writer contexts together (~ lg(NumThreads) serial steps). 283 unsigned Mid = Contexts.size() / 2; 284 unsigned End = Contexts.size(); 285 assert(Mid > 0 && "Expected more than one context"); 286 do { 287 for (unsigned I = 0; I < Mid; ++I) 288 Pool.async(mergeWriterContexts, Contexts[I].get(), 289 Contexts[I + Mid].get()); 290 Pool.wait(); 291 if (End & 1) { 292 Pool.async(mergeWriterContexts, Contexts[0].get(), 293 Contexts[End - 1].get()); 294 Pool.wait(); 295 } 296 End = Mid; 297 Mid /= 2; 298 } while (Mid > 0); 299 } 300 301 // Handle deferred hard errors encountered during merging. 302 for (std::unique_ptr<WriterContext> &WC : Contexts) { 303 if (!WC->Err) 304 continue; 305 if (!WC->Err.isA<InstrProfError>()) 306 exitWithError(std::move(WC->Err), WC->ErrWhence); 307 308 instrprof_error IPE = InstrProfError::take(std::move(WC->Err)); 309 if (isFatalError(IPE)) 310 exitWithError(make_error<InstrProfError>(IPE), WC->ErrWhence); 311 else 312 warn(toString(make_error<InstrProfError>(IPE)), 313 WC->ErrWhence); 314 } 315 316 InstrProfWriter &Writer = Contexts[0]->Writer; 317 if (OutputFormat == PF_Text) { 318 if (Error E = Writer.writeText(Output)) 319 exitWithError(std::move(E)); 320 } else { 321 Writer.write(Output); 322 } 323 } 324 325 static sampleprof::SampleProfileFormat FormatMap[] = { 326 sampleprof::SPF_None, sampleprof::SPF_Text, sampleprof::SPF_Compact_Binary, 327 sampleprof::SPF_GCC, sampleprof::SPF_Binary}; 328 329 static void mergeSampleProfile(const WeightedFileVector &Inputs, 330 StringRef OutputFilename, 331 ProfileFormat OutputFormat) { 332 using namespace sampleprof; 333 auto WriterOrErr = 334 SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]); 335 if (std::error_code EC = WriterOrErr.getError()) 336 exitWithErrorCode(EC, OutputFilename); 337 338 auto Writer = std::move(WriterOrErr.get()); 339 StringMap<FunctionSamples> ProfileMap; 340 SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers; 341 LLVMContext Context; 342 for (const auto &Input : Inputs) { 343 auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context); 344 if (std::error_code EC = ReaderOrErr.getError()) 345 exitWithErrorCode(EC, Input.Filename); 346 347 // We need to keep the readers around until after all the files are 348 // read so that we do not lose the function names stored in each 349 // reader's memory. The function names are needed to write out the 350 // merged profile map. 351 Readers.push_back(std::move(ReaderOrErr.get())); 352 const auto Reader = Readers.back().get(); 353 if (std::error_code EC = Reader->read()) 354 exitWithErrorCode(EC, Input.Filename); 355 356 StringMap<FunctionSamples> &Profiles = Reader->getProfiles(); 357 for (StringMap<FunctionSamples>::iterator I = Profiles.begin(), 358 E = Profiles.end(); 359 I != E; ++I) { 360 StringRef FName = I->first(); 361 FunctionSamples &Samples = I->second; 362 sampleprof_error Result = ProfileMap[FName].merge(Samples, Input.Weight); 363 if (Result != sampleprof_error::success) { 364 std::error_code EC = make_error_code(Result); 365 handleMergeWriterError(errorCodeToError(EC), Input.Filename, FName); 366 } 367 } 368 } 369 Writer->write(ProfileMap); 370 } 371 372 static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) { 373 StringRef WeightStr, FileName; 374 std::tie(WeightStr, FileName) = WeightedFilename.split(','); 375 376 uint64_t Weight; 377 if (WeightStr.getAsInteger(10, Weight) || Weight < 1) 378 exitWithError("Input weight must be a positive integer."); 379 380 return {FileName, Weight}; 381 } 382 383 static std::unique_ptr<MemoryBuffer> 384 getInputFilenamesFileBuf(const StringRef &InputFilenamesFile) { 385 if (InputFilenamesFile == "") 386 return {}; 387 388 auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFilenamesFile); 389 if (!BufOrError) 390 exitWithErrorCode(BufOrError.getError(), InputFilenamesFile); 391 392 return std::move(*BufOrError); 393 } 394 395 static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) { 396 StringRef Filename = WF.Filename; 397 uint64_t Weight = WF.Weight; 398 399 // If it's STDIN just pass it on. 400 if (Filename == "-") { 401 WNI.push_back({Filename, Weight}); 402 return; 403 } 404 405 llvm::sys::fs::file_status Status; 406 llvm::sys::fs::status(Filename, Status); 407 if (!llvm::sys::fs::exists(Status)) 408 exitWithErrorCode(make_error_code(errc::no_such_file_or_directory), 409 Filename); 410 // If it's a source file, collect it. 411 if (llvm::sys::fs::is_regular_file(Status)) { 412 WNI.push_back({Filename, Weight}); 413 return; 414 } 415 416 if (llvm::sys::fs::is_directory(Status)) { 417 std::error_code EC; 418 for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E; 419 F != E && !EC; F.increment(EC)) { 420 if (llvm::sys::fs::is_regular_file(F->path())) { 421 addWeightedInput(WNI, {F->path(), Weight}); 422 } 423 } 424 if (EC) 425 exitWithErrorCode(EC, Filename); 426 } 427 } 428 429 static void parseInputFilenamesFile(MemoryBuffer *Buffer, 430 WeightedFileVector &WFV) { 431 if (!Buffer) 432 return; 433 434 SmallVector<StringRef, 8> Entries; 435 StringRef Data = Buffer->getBuffer(); 436 Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); 437 for (const StringRef &FileWeightEntry : Entries) { 438 StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r"); 439 // Skip comments. 440 if (SanitizedEntry.startswith("#")) 441 continue; 442 // If there's no comma, it's an unweighted profile. 443 else if (SanitizedEntry.find(',') == StringRef::npos) 444 addWeightedInput(WFV, {SanitizedEntry, 1}); 445 else 446 addWeightedInput(WFV, parseWeightedFile(SanitizedEntry)); 447 } 448 } 449 450 static int merge_main(int argc, const char *argv[]) { 451 cl::list<std::string> InputFilenames(cl::Positional, 452 cl::desc("<filename...>")); 453 cl::list<std::string> WeightedInputFilenames("weighted-input", 454 cl::desc("<weight>,<filename>")); 455 cl::opt<std::string> InputFilenamesFile( 456 "input-files", cl::init(""), 457 cl::desc("Path to file containing newline-separated " 458 "[<weight>,]<filename> entries")); 459 cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"), 460 cl::aliasopt(InputFilenamesFile)); 461 cl::opt<bool> DumpInputFileList( 462 "dump-input-file-list", cl::init(false), cl::Hidden, 463 cl::desc("Dump the list of input files and their weights, then exit")); 464 cl::opt<std::string> OutputFilename("output", cl::value_desc("output"), 465 cl::init("-"), cl::Required, 466 cl::desc("Output file")); 467 cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), 468 cl::aliasopt(OutputFilename)); 469 cl::opt<ProfileKinds> ProfileKind( 470 cl::desc("Profile kind:"), cl::init(instr), 471 cl::values(clEnumVal(instr, "Instrumentation profile (default)"), 472 clEnumVal(sample, "Sample profile"))); 473 cl::opt<ProfileFormat> OutputFormat( 474 cl::desc("Format of output profile"), cl::init(PF_Binary), 475 cl::values(clEnumValN(PF_Binary, "binary", "Binary encoding (default)"), 476 clEnumValN(PF_Compact_Binary, "compbinary", 477 "Compact binary encoding"), 478 clEnumValN(PF_Text, "text", "Text encoding"), 479 clEnumValN(PF_GCC, "gcc", 480 "GCC encoding (only meaningful for -sample)"))); 481 cl::opt<bool> OutputSparse("sparse", cl::init(false), 482 cl::desc("Generate a sparse profile (only meaningful for -instr)")); 483 cl::opt<unsigned> NumThreads( 484 "num-threads", cl::init(0), 485 cl::desc("Number of merge threads to use (default: autodetect)")); 486 cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), 487 cl::aliasopt(NumThreads)); 488 489 cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); 490 491 WeightedFileVector WeightedInputs; 492 for (StringRef Filename : InputFilenames) 493 addWeightedInput(WeightedInputs, {Filename, 1}); 494 for (StringRef WeightedFilename : WeightedInputFilenames) 495 addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename)); 496 497 // Make sure that the file buffer stays alive for the duration of the 498 // weighted input vector's lifetime. 499 auto Buffer = getInputFilenamesFileBuf(InputFilenamesFile); 500 parseInputFilenamesFile(Buffer.get(), WeightedInputs); 501 502 if (WeightedInputs.empty()) 503 exitWithError("No input files specified. See " + 504 sys::path::filename(argv[0]) + " -help"); 505 506 if (DumpInputFileList) { 507 for (auto &WF : WeightedInputs) 508 outs() << WF.Weight << "," << WF.Filename << "\n"; 509 return 0; 510 } 511 512 if (ProfileKind == instr) 513 mergeInstrProfile(WeightedInputs, OutputFilename, OutputFormat, 514 OutputSparse, NumThreads); 515 else 516 mergeSampleProfile(WeightedInputs, OutputFilename, OutputFormat); 517 518 return 0; 519 } 520 521 typedef struct ValueSitesStats { 522 ValueSitesStats() 523 : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0), 524 TotalNumValues(0) {} 525 uint64_t TotalNumValueSites; 526 uint64_t TotalNumValueSitesWithValueProfile; 527 uint64_t TotalNumValues; 528 std::vector<unsigned> ValueSitesHistogram; 529 } ValueSitesStats; 530 531 static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK, 532 ValueSitesStats &Stats, raw_fd_ostream &OS, 533 InstrProfSymtab *Symtab) { 534 uint32_t NS = Func.getNumValueSites(VK); 535 Stats.TotalNumValueSites += NS; 536 for (size_t I = 0; I < NS; ++I) { 537 uint32_t NV = Func.getNumValueDataForSite(VK, I); 538 std::unique_ptr<InstrProfValueData[]> VD = Func.getValueForSite(VK, I); 539 Stats.TotalNumValues += NV; 540 if (NV) { 541 Stats.TotalNumValueSitesWithValueProfile++; 542 if (NV > Stats.ValueSitesHistogram.size()) 543 Stats.ValueSitesHistogram.resize(NV, 0); 544 Stats.ValueSitesHistogram[NV - 1]++; 545 } 546 for (uint32_t V = 0; V < NV; V++) { 547 OS << "\t[ " << I << ", "; 548 if (Symtab == nullptr) 549 OS << VD[V].Value; 550 else 551 OS << Symtab->getFuncName(VD[V].Value); 552 OS << ", " << VD[V].Count << " ]\n"; 553 } 554 } 555 } 556 557 static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK, 558 ValueSitesStats &Stats) { 559 OS << " Total number of sites: " << Stats.TotalNumValueSites << "\n"; 560 OS << " Total number of sites with values: " 561 << Stats.TotalNumValueSitesWithValueProfile << "\n"; 562 OS << " Total number of profiled values: " << Stats.TotalNumValues << "\n"; 563 564 OS << " Value sites histogram:\n\tNumTargets, SiteCount\n"; 565 for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) { 566 if (Stats.ValueSitesHistogram[I] > 0) 567 OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n"; 568 } 569 } 570 571 static int showInstrProfile(const std::string &Filename, bool ShowCounts, 572 uint32_t TopN, bool ShowIndirectCallTargets, 573 bool ShowMemOPSizes, bool ShowDetailedSummary, 574 std::vector<uint32_t> DetailedSummaryCutoffs, 575 bool ShowAllFunctions, 576 const std::string &ShowFunction, bool TextFormat, 577 raw_fd_ostream &OS) { 578 auto ReaderOrErr = InstrProfReader::create(Filename); 579 std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs); 580 if (ShowDetailedSummary && Cutoffs.empty()) { 581 Cutoffs = {800000, 900000, 950000, 990000, 999000, 999900, 999990}; 582 } 583 InstrProfSummaryBuilder Builder(std::move(Cutoffs)); 584 if (Error E = ReaderOrErr.takeError()) 585 exitWithError(std::move(E), Filename); 586 587 auto Reader = std::move(ReaderOrErr.get()); 588 bool IsIRInstr = Reader->isIRLevelProfile(); 589 size_t ShownFunctions = 0; 590 int NumVPKind = IPVK_Last - IPVK_First + 1; 591 std::vector<ValueSitesStats> VPStats(NumVPKind); 592 593 auto MinCmp = [](const std::pair<std::string, uint64_t> &v1, 594 const std::pair<std::string, uint64_t> &v2) { 595 return v1.second > v2.second; 596 }; 597 598 std::priority_queue<std::pair<std::string, uint64_t>, 599 std::vector<std::pair<std::string, uint64_t>>, 600 decltype(MinCmp)> 601 HottestFuncs(MinCmp); 602 603 // Add marker so that IR-level instrumentation round-trips properly. 604 if (TextFormat && IsIRInstr) 605 OS << ":ir\n"; 606 607 for (const auto &Func : *Reader) { 608 bool Show = 609 ShowAllFunctions || (!ShowFunction.empty() && 610 Func.Name.find(ShowFunction) != Func.Name.npos); 611 612 bool doTextFormatDump = (Show && TextFormat); 613 614 if (doTextFormatDump) { 615 InstrProfSymtab &Symtab = Reader->getSymtab(); 616 InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab, 617 OS); 618 continue; 619 } 620 621 assert(Func.Counts.size() > 0 && "function missing entry counter"); 622 Builder.addRecord(Func); 623 624 if (TopN) { 625 uint64_t FuncMax = 0; 626 for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) 627 FuncMax = std::max(FuncMax, Func.Counts[I]); 628 629 if (HottestFuncs.size() == TopN) { 630 if (HottestFuncs.top().second < FuncMax) { 631 HottestFuncs.pop(); 632 HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); 633 } 634 } else 635 HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); 636 } 637 638 if (Show) { 639 640 if (!ShownFunctions) 641 OS << "Counters:\n"; 642 643 ++ShownFunctions; 644 645 OS << " " << Func.Name << ":\n" 646 << " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n" 647 << " Counters: " << Func.Counts.size() << "\n"; 648 if (!IsIRInstr) 649 OS << " Function count: " << Func.Counts[0] << "\n"; 650 651 if (ShowIndirectCallTargets) 652 OS << " Indirect Call Site Count: " 653 << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n"; 654 655 uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize); 656 if (ShowMemOPSizes && NumMemOPCalls > 0) 657 OS << " Number of Memory Intrinsics Calls: " << NumMemOPCalls 658 << "\n"; 659 660 if (ShowCounts) { 661 OS << " Block counts: ["; 662 size_t Start = (IsIRInstr ? 0 : 1); 663 for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) { 664 OS << (I == Start ? "" : ", ") << Func.Counts[I]; 665 } 666 OS << "]\n"; 667 } 668 669 if (ShowIndirectCallTargets) { 670 OS << " Indirect Target Results:\n"; 671 traverseAllValueSites(Func, IPVK_IndirectCallTarget, 672 VPStats[IPVK_IndirectCallTarget], OS, 673 &(Reader->getSymtab())); 674 } 675 676 if (ShowMemOPSizes && NumMemOPCalls > 0) { 677 OS << " Memory Intrinsic Size Results:\n"; 678 traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS, 679 nullptr); 680 } 681 } 682 } 683 if (Reader->hasError()) 684 exitWithError(Reader->getError(), Filename); 685 686 if (TextFormat) 687 return 0; 688 std::unique_ptr<ProfileSummary> PS(Builder.getSummary()); 689 OS << "Instrumentation level: " 690 << (Reader->isIRLevelProfile() ? "IR" : "Front-end") << "\n"; 691 if (ShowAllFunctions || !ShowFunction.empty()) 692 OS << "Functions shown: " << ShownFunctions << "\n"; 693 OS << "Total functions: " << PS->getNumFunctions() << "\n"; 694 OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n"; 695 OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n"; 696 697 if (TopN) { 698 std::vector<std::pair<std::string, uint64_t>> SortedHottestFuncs; 699 while (!HottestFuncs.empty()) { 700 SortedHottestFuncs.emplace_back(HottestFuncs.top()); 701 HottestFuncs.pop(); 702 } 703 OS << "Top " << TopN 704 << " functions with the largest internal block counts: \n"; 705 for (auto &hotfunc : llvm::reverse(SortedHottestFuncs)) 706 OS << " " << hotfunc.first << ", max count = " << hotfunc.second << "\n"; 707 } 708 709 if (ShownFunctions && ShowIndirectCallTargets) { 710 OS << "Statistics for indirect call sites profile:\n"; 711 showValueSitesStats(OS, IPVK_IndirectCallTarget, 712 VPStats[IPVK_IndirectCallTarget]); 713 } 714 715 if (ShownFunctions && ShowMemOPSizes) { 716 OS << "Statistics for memory intrinsic calls sizes profile:\n"; 717 showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]); 718 } 719 720 if (ShowDetailedSummary) { 721 OS << "Detailed summary:\n"; 722 OS << "Total number of blocks: " << PS->getNumCounts() << "\n"; 723 OS << "Total count: " << PS->getTotalCount() << "\n"; 724 for (auto Entry : PS->getDetailedSummary()) { 725 OS << Entry.NumCounts << " blocks with count >= " << Entry.MinCount 726 << " account for " 727 << format("%0.6g", (float)Entry.Cutoff / ProfileSummary::Scale * 100) 728 << " percentage of the total counts.\n"; 729 } 730 } 731 return 0; 732 } 733 734 static int showSampleProfile(const std::string &Filename, bool ShowCounts, 735 bool ShowAllFunctions, 736 const std::string &ShowFunction, 737 raw_fd_ostream &OS) { 738 using namespace sampleprof; 739 LLVMContext Context; 740 auto ReaderOrErr = SampleProfileReader::create(Filename, Context); 741 if (std::error_code EC = ReaderOrErr.getError()) 742 exitWithErrorCode(EC, Filename); 743 744 auto Reader = std::move(ReaderOrErr.get()); 745 if (std::error_code EC = Reader->read()) 746 exitWithErrorCode(EC, Filename); 747 748 if (ShowAllFunctions || ShowFunction.empty()) 749 Reader->dump(OS); 750 else 751 Reader->dumpFunctionProfile(ShowFunction, OS); 752 753 return 0; 754 } 755 756 static int show_main(int argc, const char *argv[]) { 757 cl::opt<std::string> Filename(cl::Positional, cl::Required, 758 cl::desc("<profdata-file>")); 759 760 cl::opt<bool> ShowCounts("counts", cl::init(false), 761 cl::desc("Show counter values for shown functions")); 762 cl::opt<bool> TextFormat( 763 "text", cl::init(false), 764 cl::desc("Show instr profile data in text dump format")); 765 cl::opt<bool> ShowIndirectCallTargets( 766 "ic-targets", cl::init(false), 767 cl::desc("Show indirect call site target values for shown functions")); 768 cl::opt<bool> ShowMemOPSizes( 769 "memop-sizes", cl::init(false), 770 cl::desc("Show the profiled sizes of the memory intrinsic calls " 771 "for shown functions")); 772 cl::opt<bool> ShowDetailedSummary("detailed-summary", cl::init(false), 773 cl::desc("Show detailed profile summary")); 774 cl::list<uint32_t> DetailedSummaryCutoffs( 775 cl::CommaSeparated, "detailed-summary-cutoffs", 776 cl::desc( 777 "Cutoff percentages (times 10000) for generating detailed summary"), 778 cl::value_desc("800000,901000,999999")); 779 cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false), 780 cl::desc("Details for every function")); 781 cl::opt<std::string> ShowFunction("function", 782 cl::desc("Details for matching functions")); 783 784 cl::opt<std::string> OutputFilename("output", cl::value_desc("output"), 785 cl::init("-"), cl::desc("Output file")); 786 cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), 787 cl::aliasopt(OutputFilename)); 788 cl::opt<ProfileKinds> ProfileKind( 789 cl::desc("Profile kind:"), cl::init(instr), 790 cl::values(clEnumVal(instr, "Instrumentation profile (default)"), 791 clEnumVal(sample, "Sample profile"))); 792 cl::opt<uint32_t> TopNFunctions( 793 "topn", cl::init(0), 794 cl::desc("Show the list of functions with the largest internal counts")); 795 796 cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); 797 798 if (OutputFilename.empty()) 799 OutputFilename = "-"; 800 801 std::error_code EC; 802 raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::F_Text); 803 if (EC) 804 exitWithErrorCode(EC, OutputFilename); 805 806 if (ShowAllFunctions && !ShowFunction.empty()) 807 WithColor::warning() << "-function argument ignored: showing all functions\n"; 808 809 std::vector<uint32_t> Cutoffs(DetailedSummaryCutoffs.begin(), 810 DetailedSummaryCutoffs.end()); 811 if (ProfileKind == instr) 812 return showInstrProfile(Filename, ShowCounts, TopNFunctions, 813 ShowIndirectCallTargets, ShowMemOPSizes, 814 ShowDetailedSummary, DetailedSummaryCutoffs, 815 ShowAllFunctions, ShowFunction, TextFormat, OS); 816 else 817 return showSampleProfile(Filename, ShowCounts, ShowAllFunctions, 818 ShowFunction, OS); 819 } 820 821 int main(int argc, const char *argv[]) { 822 InitLLVM X(argc, argv); 823 824 StringRef ProgName(sys::path::filename(argv[0])); 825 if (argc > 1) { 826 int (*func)(int, const char *[]) = nullptr; 827 828 if (strcmp(argv[1], "merge") == 0) 829 func = merge_main; 830 else if (strcmp(argv[1], "show") == 0) 831 func = show_main; 832 833 if (func) { 834 std::string Invocation(ProgName.str() + " " + argv[1]); 835 argv[1] = Invocation.c_str(); 836 return func(argc - 1, argv + 1); 837 } 838 839 if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 || 840 strcmp(argv[1], "--help") == 0) { 841 842 errs() << "OVERVIEW: LLVM profile data tools\n\n" 843 << "USAGE: " << ProgName << " <command> [args...]\n" 844 << "USAGE: " << ProgName << " <command> -help\n\n" 845 << "See each individual command --help for more details.\n" 846 << "Available commands: merge, show\n"; 847 return 0; 848 } 849 } 850 851 if (argc < 2) 852 errs() << ProgName << ": No command specified!\n"; 853 else 854 errs() << ProgName << ": Unknown command!\n"; 855 856 errs() << "USAGE: " << ProgName << " <merge|show> [args...]\n"; 857 return 1; 858 } 859