1846a627fSDuncan P. N. Exon Smith //===- llvm-profdata.cpp - LLVM profile data tool -------------------------===// 2846a627fSDuncan P. N. Exon Smith // 32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6846a627fSDuncan P. N. Exon Smith // 7846a627fSDuncan P. N. Exon Smith //===----------------------------------------------------------------------===// 8846a627fSDuncan P. N. Exon Smith // 9846a627fSDuncan P. N. Exon Smith // llvm-profdata merges .profdata files. 10846a627fSDuncan P. N. Exon Smith // 11846a627fSDuncan P. N. Exon Smith //===----------------------------------------------------------------------===// 12846a627fSDuncan P. N. Exon Smith 13c21a44daSNathan Slingerland #include "llvm/ADT/SmallSet.h" 147f5b47ddSNathan Slingerland #include "llvm/ADT/SmallVector.h" 15846a627fSDuncan P. N. Exon Smith #include "llvm/ADT/StringRef.h" 16d9903888SChandler Carruth #include "llvm/IR/LLVMContext.h" 17f8d79198SJustin Bogner #include "llvm/ProfileData/InstrProfReader.h" 18b9bd7f85SJustin Bogner #include "llvm/ProfileData/InstrProfWriter.h" 19d68aae24SEaswaran Raman #include "llvm/ProfileData/ProfileCommon.h" 20d5336ae2SDiego Novillo #include "llvm/ProfileData/SampleProfReader.h" 21d5336ae2SDiego Novillo #include "llvm/ProfileData/SampleProfWriter.h" 22846a627fSDuncan P. N. Exon Smith #include "llvm/Support/CommandLine.h" 237f5b47ddSNathan Slingerland #include "llvm/Support/Errc.h" 24d59664f4SBenjamin Kramer #include "llvm/Support/FileSystem.h" 25423380f9SJustin Bogner #include "llvm/Support/Format.h" 2653cf5302Sweihe #include "llvm/Support/FormattedStream.h" 27197194b6SRui Ueyama #include "llvm/Support/InitLLVM.h" 28846a627fSDuncan P. N. Exon Smith #include "llvm/Support/MemoryBuffer.h" 2916132e6fSBenjamin Kramer #include "llvm/Support/Path.h" 305342dd6bSBruno Ricci #include "llvm/Support/ThreadPool.h" 3153cf5302Sweihe #include "llvm/Support/Threading.h" 32ef598759SFangrui Song #include "llvm/Support/WithColor.h" 33846a627fSDuncan P. N. Exon Smith #include "llvm/Support/raw_ostream.h" 347f5b47ddSNathan Slingerland #include <algorithm> 35846a627fSDuncan P. N. Exon Smith 36846a627fSDuncan P. N. Exon Smith using namespace llvm; 37846a627fSDuncan P. N. Exon Smith 38a0c0857eSWei Mi enum ProfileFormat { 39a0c0857eSWei Mi PF_None = 0, 40a0c0857eSWei Mi PF_Text, 41a0c0857eSWei Mi PF_Compact_Binary, 42be907324SWei Mi PF_Ext_Binary, 43a0c0857eSWei Mi PF_GCC, 44d9be2c7eSWei Mi PF_Binary 45a0c0857eSWei Mi }; 466f7c19a4SXinliang David Li 47e46b7565SJonas Devlieghere static void warn(Twine Message, std::string Whence = "", 48faaa42adSVedant Kumar std::string Hint = "") { 49e46b7565SJonas Devlieghere WithColor::warning(); 50f8d79198SJustin Bogner if (!Whence.empty()) 51f8d79198SJustin Bogner errs() << Whence << ": "; 52f8d79198SJustin Bogner errs() << Message << "\n"; 534f823667SNathan Slingerland if (!Hint.empty()) 54e46b7565SJonas Devlieghere WithColor::note() << Hint << "\n"; 55188efda5SVedant Kumar } 56188efda5SVedant Kumar 57188efda5SVedant Kumar static void exitWithError(Twine Message, std::string Whence = "", 58188efda5SVedant Kumar std::string Hint = "") { 59e46b7565SJonas Devlieghere WithColor::error(); 60e46b7565SJonas Devlieghere if (!Whence.empty()) 61e46b7565SJonas Devlieghere errs() << Whence << ": "; 62e46b7565SJonas Devlieghere errs() << Message << "\n"; 63e46b7565SJonas Devlieghere if (!Hint.empty()) 64e46b7565SJonas Devlieghere WithColor::note() << Hint << "\n"; 65846a627fSDuncan P. N. Exon Smith ::exit(1); 66846a627fSDuncan P. N. Exon Smith } 67846a627fSDuncan P. N. Exon Smith 689152fd17SVedant Kumar static void exitWithError(Error E, StringRef Whence = "") { 699152fd17SVedant Kumar if (E.isA<InstrProfError>()) { 709152fd17SVedant Kumar handleAllErrors(std::move(E), [&](const InstrProfError &IPE) { 719152fd17SVedant Kumar instrprof_error instrError = IPE.get(); 729152fd17SVedant Kumar StringRef Hint = ""; 734f823667SNathan Slingerland if (instrError == instrprof_error::unrecognized_format) { 7401bfb366SYi Kong // Hint for common error of forgetting --sample for sample profiles. 7501bfb366SYi Kong Hint = "Perhaps you forgot to use the --sample option?"; 764f823667SNathan Slingerland } 77adcd0268SBenjamin Kramer exitWithError(IPE.message(), std::string(Whence), std::string(Hint)); 789152fd17SVedant Kumar }); 794f823667SNathan Slingerland } 809152fd17SVedant Kumar 81adcd0268SBenjamin Kramer exitWithError(toString(std::move(E)), std::string(Whence)); 829152fd17SVedant Kumar } 839152fd17SVedant Kumar 849152fd17SVedant Kumar static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") { 85adcd0268SBenjamin Kramer exitWithError(EC.message(), std::string(Whence)); 864f823667SNathan Slingerland } 874f823667SNathan Slingerland 8802b6fa90SDuncan P. N. Exon Smith namespace { 89d5336ae2SDiego Novillo enum ProfileKinds { instr, sample }; 900fcfe897SVedant Kumar enum FailureMode { failIfAnyAreInvalid, failIfAllAreInvalid }; 910fcfe897SVedant Kumar } 920fcfe897SVedant Kumar 930fcfe897SVedant Kumar static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC, 940fcfe897SVedant Kumar StringRef Whence = "") { 950fcfe897SVedant Kumar if (FailMode == failIfAnyAreInvalid) 960fcfe897SVedant Kumar exitWithErrorCode(EC, Whence); 970fcfe897SVedant Kumar else 98adcd0268SBenjamin Kramer warn(EC.message(), std::string(Whence)); 9902b6fa90SDuncan P. N. Exon Smith } 100618bcea7SJustin Bogner 1019152fd17SVedant Kumar static void handleMergeWriterError(Error E, StringRef WhenceFile = "", 102e6e30d5eSNathan Slingerland StringRef WhenceFunction = "", 103d3babdbcSDiego Novillo bool ShowHint = true) { 104e6e30d5eSNathan Slingerland if (!WhenceFile.empty()) 105e6e30d5eSNathan Slingerland errs() << WhenceFile << ": "; 106e6e30d5eSNathan Slingerland if (!WhenceFunction.empty()) 107e6e30d5eSNathan Slingerland errs() << WhenceFunction << ": "; 1089152fd17SVedant Kumar 1099152fd17SVedant Kumar auto IPE = instrprof_error::success; 1109152fd17SVedant Kumar E = handleErrors(std::move(E), 1119152fd17SVedant Kumar [&IPE](std::unique_ptr<InstrProfError> E) -> Error { 1129152fd17SVedant Kumar IPE = E->get(); 1139152fd17SVedant Kumar return Error(std::move(E)); 1149152fd17SVedant Kumar }); 1159152fd17SVedant Kumar errs() << toString(std::move(E)) << "\n"; 116e6e30d5eSNathan Slingerland 117e6e30d5eSNathan Slingerland if (ShowHint) { 118e6e30d5eSNathan Slingerland StringRef Hint = ""; 1199152fd17SVedant Kumar if (IPE != instrprof_error::success) { 1209152fd17SVedant Kumar switch (IPE) { 12111c938d1SNathan Slingerland case instrprof_error::hash_mismatch: 12211c938d1SNathan Slingerland case instrprof_error::count_mismatch: 12311c938d1SNathan Slingerland case instrprof_error::value_site_count_mismatch: 124d3babdbcSDiego Novillo Hint = "Make sure that all profile data to be merged is generated " 125e6e30d5eSNathan Slingerland "from the same binary."; 12611c938d1SNathan Slingerland break; 127b2d95f0dSNathan Slingerland default: 128b2d95f0dSNathan Slingerland break; 129e6e30d5eSNathan Slingerland } 130e6e30d5eSNathan Slingerland } 131e6e30d5eSNathan Slingerland 132e6e30d5eSNathan Slingerland if (!Hint.empty()) 133e6e30d5eSNathan Slingerland errs() << Hint << "\n"; 134e6e30d5eSNathan Slingerland } 135e6e30d5eSNathan Slingerland } 136e6e30d5eSNathan Slingerland 1373164fcfdSRichard Smith namespace { 1383164fcfdSRichard Smith /// A remapper from original symbol names to new symbol names based on a file 1393164fcfdSRichard Smith /// containing a list of mappings from old name to new name. 1403164fcfdSRichard Smith class SymbolRemapper { 1413164fcfdSRichard Smith std::unique_ptr<MemoryBuffer> File; 1423164fcfdSRichard Smith DenseMap<StringRef, StringRef> RemappingTable; 1433164fcfdSRichard Smith 1443164fcfdSRichard Smith public: 1453164fcfdSRichard Smith /// Build a SymbolRemapper from a file containing a list of old/new symbols. 1463164fcfdSRichard Smith static std::unique_ptr<SymbolRemapper> create(StringRef InputFile) { 1473164fcfdSRichard Smith auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile); 1483164fcfdSRichard Smith if (!BufOrError) 1493164fcfdSRichard Smith exitWithErrorCode(BufOrError.getError(), InputFile); 1503164fcfdSRichard Smith 1510eaee545SJonas Devlieghere auto Remapper = std::make_unique<SymbolRemapper>(); 1523164fcfdSRichard Smith Remapper->File = std::move(BufOrError.get()); 1533164fcfdSRichard Smith 1543164fcfdSRichard Smith for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#'); 1553164fcfdSRichard Smith !LineIt.is_at_eof(); ++LineIt) { 1563164fcfdSRichard Smith std::pair<StringRef, StringRef> Parts = LineIt->split(' '); 1573164fcfdSRichard Smith if (Parts.first.empty() || Parts.second.empty() || 1583164fcfdSRichard Smith Parts.second.count(' ')) { 1593164fcfdSRichard Smith exitWithError("unexpected line in remapping file", 1603164fcfdSRichard Smith (InputFile + ":" + Twine(LineIt.line_number())).str(), 1613164fcfdSRichard Smith "expected 'old_symbol new_symbol'"); 1623164fcfdSRichard Smith } 1633164fcfdSRichard Smith Remapper->RemappingTable.insert(Parts); 1643164fcfdSRichard Smith } 1653164fcfdSRichard Smith return Remapper; 1663164fcfdSRichard Smith } 1673164fcfdSRichard Smith 1683164fcfdSRichard Smith /// Attempt to map the given old symbol into a new symbol. 1693164fcfdSRichard Smith /// 1703164fcfdSRichard Smith /// \return The new symbol, or \p Name if no such symbol was found. 1713164fcfdSRichard Smith StringRef operator()(StringRef Name) { 1723164fcfdSRichard Smith StringRef New = RemappingTable.lookup(Name); 1733164fcfdSRichard Smith return New.empty() ? Name : New; 1743164fcfdSRichard Smith } 1753164fcfdSRichard Smith }; 1763164fcfdSRichard Smith } 1773164fcfdSRichard Smith 1787f5b47ddSNathan Slingerland struct WeightedFile { 1799a1bfcfaSXinliang David Li std::string Filename; 1807f5b47ddSNathan Slingerland uint64_t Weight; 1817f5b47ddSNathan Slingerland }; 1827f5b47ddSNathan Slingerland typedef SmallVector<WeightedFile, 5> WeightedFileVector; 1837f5b47ddSNathan Slingerland 184e3a0bf50SVedant Kumar /// Keep track of merged data and reported errors. 185e3a0bf50SVedant Kumar struct WriterContext { 186e3a0bf50SVedant Kumar std::mutex Lock; 187e3a0bf50SVedant Kumar InstrProfWriter Writer; 1880fcfe897SVedant Kumar std::vector<std::pair<Error, std::string>> Errors; 189e3a0bf50SVedant Kumar std::mutex &ErrLock; 190e3a0bf50SVedant Kumar SmallSet<instrprof_error, 4> &WriterErrorCodes; 191e3a0bf50SVedant Kumar 192e3a0bf50SVedant Kumar WriterContext(bool IsSparse, std::mutex &ErrLock, 193e3a0bf50SVedant Kumar SmallSet<instrprof_error, 4> &WriterErrorCodes) 1940fcfe897SVedant Kumar : Lock(), Writer(IsSparse), Errors(), ErrLock(ErrLock), 1950fcfe897SVedant Kumar WriterErrorCodes(WriterErrorCodes) {} 196e3a0bf50SVedant Kumar }; 197e3a0bf50SVedant Kumar 198998b97f6SRong Xu /// Computer the overlap b/w profile BaseFilename and TestFileName, 199998b97f6SRong Xu /// and store the program level result to Overlap. 200998b97f6SRong Xu static void overlapInput(const std::string &BaseFilename, 201998b97f6SRong Xu const std::string &TestFilename, WriterContext *WC, 202998b97f6SRong Xu OverlapStats &Overlap, 203998b97f6SRong Xu const OverlapFuncFilters &FuncFilter, 204998b97f6SRong Xu raw_fd_ostream &OS, bool IsCS) { 205998b97f6SRong Xu auto ReaderOrErr = InstrProfReader::create(TestFilename); 206998b97f6SRong Xu if (Error E = ReaderOrErr.takeError()) { 207998b97f6SRong Xu // Skip the empty profiles by returning sliently. 208998b97f6SRong Xu instrprof_error IPE = InstrProfError::take(std::move(E)); 209998b97f6SRong Xu if (IPE != instrprof_error::empty_raw_profile) 2100fcfe897SVedant Kumar WC->Errors.emplace_back(make_error<InstrProfError>(IPE), TestFilename); 211998b97f6SRong Xu return; 212998b97f6SRong Xu } 213998b97f6SRong Xu 214998b97f6SRong Xu auto Reader = std::move(ReaderOrErr.get()); 215998b97f6SRong Xu for (auto &I : *Reader) { 216998b97f6SRong Xu OverlapStats FuncOverlap(OverlapStats::FunctionLevel); 217998b97f6SRong Xu FuncOverlap.setFuncInfo(I.Name, I.Hash); 218998b97f6SRong Xu 219998b97f6SRong Xu WC->Writer.overlapRecord(std::move(I), Overlap, FuncOverlap, FuncFilter); 220998b97f6SRong Xu FuncOverlap.dump(OS); 221998b97f6SRong Xu } 222998b97f6SRong Xu } 223998b97f6SRong Xu 224e3a0bf50SVedant Kumar /// Load an input into a writer context. 2253164fcfdSRichard Smith static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, 2263164fcfdSRichard Smith WriterContext *WC) { 227e3a0bf50SVedant Kumar std::unique_lock<std::mutex> CtxGuard{WC->Lock}; 228e3a0bf50SVedant Kumar 229faaa42adSVedant Kumar // Copy the filename, because llvm::ThreadPool copied the input "const 230faaa42adSVedant Kumar // WeightedFile &" by value, making a reference to the filename within it 231faaa42adSVedant Kumar // invalid outside of this packaged task. 2320fcfe897SVedant Kumar std::string Filename = Input.Filename; 233e3a0bf50SVedant Kumar 234e3a0bf50SVedant Kumar auto ReaderOrErr = InstrProfReader::create(Input.Filename); 2352c684cfdSRong Xu if (Error E = ReaderOrErr.takeError()) { 2362c684cfdSRong Xu // Skip the empty profiles by returning sliently. 2372c684cfdSRong Xu instrprof_error IPE = InstrProfError::take(std::move(E)); 2382c684cfdSRong Xu if (IPE != instrprof_error::empty_raw_profile) 2390fcfe897SVedant Kumar WC->Errors.emplace_back(make_error<InstrProfError>(IPE), Filename); 240e3a0bf50SVedant Kumar return; 2412c684cfdSRong Xu } 242e3a0bf50SVedant Kumar 243e3a0bf50SVedant Kumar auto Reader = std::move(ReaderOrErr.get()); 244e3a0bf50SVedant Kumar bool IsIRProfile = Reader->isIRLevelProfile(); 245a6ff69f6SRong Xu bool HasCSIRProfile = Reader->hasCSIRLevelProfile(); 246a6ff69f6SRong Xu if (WC->Writer.setIsIRLevelProfile(IsIRProfile, HasCSIRProfile)) { 2470fcfe897SVedant Kumar WC->Errors.emplace_back( 2480fcfe897SVedant Kumar make_error<StringError>( 249e3a0bf50SVedant Kumar "Merge IR generated profile with Clang generated profile.", 2500fcfe897SVedant Kumar std::error_code()), 2510fcfe897SVedant Kumar Filename); 252e3a0bf50SVedant Kumar return; 253e3a0bf50SVedant Kumar } 25450da55a5SRong Xu WC->Writer.setInstrEntryBBEnabled(Reader->instrEntryBBEnabled()); 255e3a0bf50SVedant Kumar 256e3a0bf50SVedant Kumar for (auto &I : *Reader) { 2573164fcfdSRichard Smith if (Remapper) 2583164fcfdSRichard Smith I.Name = (*Remapper)(I.Name); 259fe90d86cSRong Xu const StringRef FuncName = I.Name; 26098cce003SDavid Blaikie bool Reported = false; 26198cce003SDavid Blaikie WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) { 26298cce003SDavid Blaikie if (Reported) { 26398cce003SDavid Blaikie consumeError(std::move(E)); 26498cce003SDavid Blaikie return; 26598cce003SDavid Blaikie } 26698cce003SDavid Blaikie Reported = true; 267e3a0bf50SVedant Kumar // Only show hint the first time an error occurs. 268e3a0bf50SVedant Kumar instrprof_error IPE = InstrProfError::take(std::move(E)); 269e3a0bf50SVedant Kumar std::unique_lock<std::mutex> ErrGuard{WC->ErrLock}; 270e3a0bf50SVedant Kumar bool firstTime = WC->WriterErrorCodes.insert(IPE).second; 271e3a0bf50SVedant Kumar handleMergeWriterError(make_error<InstrProfError>(IPE), Input.Filename, 272fe90d86cSRong Xu FuncName, firstTime); 27398cce003SDavid Blaikie }); 274e3a0bf50SVedant Kumar } 2750fcfe897SVedant Kumar if (Reader->hasError()) 2760fcfe897SVedant Kumar if (Error E = Reader->getError()) 2770fcfe897SVedant Kumar WC->Errors.emplace_back(std::move(E), Filename); 278e3a0bf50SVedant Kumar } 279e3a0bf50SVedant Kumar 280e3a0bf50SVedant Kumar /// Merge the \p Src writer context into \p Dst. 281e3a0bf50SVedant Kumar static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) { 2820fcfe897SVedant Kumar for (auto &ErrorPair : Src->Errors) 2830fcfe897SVedant Kumar Dst->Errors.push_back(std::move(ErrorPair)); 2840fcfe897SVedant Kumar Src->Errors.clear(); 285faaa42adSVedant Kumar 28698cce003SDavid Blaikie Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) { 2870fcfe897SVedant Kumar instrprof_error IPE = InstrProfError::take(std::move(E)); 2880fcfe897SVedant Kumar std::unique_lock<std::mutex> ErrGuard{Dst->ErrLock}; 2890fcfe897SVedant Kumar bool firstTime = Dst->WriterErrorCodes.insert(IPE).second; 2900fcfe897SVedant Kumar if (firstTime) 2910fcfe897SVedant Kumar warn(toString(make_error<InstrProfError>(IPE))); 29298cce003SDavid Blaikie }); 293e3a0bf50SVedant Kumar } 294e3a0bf50SVedant Kumar 29578fe6a3eSWei Mi static void writeInstrProfile(StringRef OutputFilename, 29678fe6a3eSWei Mi ProfileFormat OutputFormat, 29778fe6a3eSWei Mi InstrProfWriter &Writer) { 29878fe6a3eSWei Mi std::error_code EC; 29978fe6a3eSWei Mi raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::OF_None); 30078fe6a3eSWei Mi if (EC) 30178fe6a3eSWei Mi exitWithErrorCode(EC, OutputFilename); 30278fe6a3eSWei Mi 30378fe6a3eSWei Mi if (OutputFormat == PF_Text) { 30478fe6a3eSWei Mi if (Error E = Writer.writeText(Output)) 30578fe6a3eSWei Mi exitWithError(std::move(E)); 30678fe6a3eSWei Mi } else { 30778fe6a3eSWei Mi Writer.write(Output); 30878fe6a3eSWei Mi } 30978fe6a3eSWei Mi } 31078fe6a3eSWei Mi 3117f5b47ddSNathan Slingerland static void mergeInstrProfile(const WeightedFileVector &Inputs, 3123164fcfdSRichard Smith SymbolRemapper *Remapper, 3136f7c19a4SXinliang David Li StringRef OutputFilename, 314e3a0bf50SVedant Kumar ProfileFormat OutputFormat, bool OutputSparse, 3150fcfe897SVedant Kumar unsigned NumThreads, FailureMode FailMode) { 316b7aa2630SJustin Bogner if (OutputFilename.compare("-") == 0) 317b7aa2630SJustin Bogner exitWithError("Cannot write indexed profdata format to stdout."); 318ec49f982SJustin Bogner 319d9be2c7eSWei Mi if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary && 320be907324SWei Mi OutputFormat != PF_Ext_Binary && OutputFormat != PF_Text) 3216f7c19a4SXinliang David Li exitWithError("Unknown format is specified."); 3226f7c19a4SXinliang David Li 323e3a0bf50SVedant Kumar std::mutex ErrorLock; 3249152fd17SVedant Kumar SmallSet<instrprof_error, 4> WriterErrorCodes; 325e3a0bf50SVedant Kumar 326e3a0bf50SVedant Kumar // If NumThreads is not specified, auto-detect a good default. 327e3a0bf50SVedant Kumar if (NumThreads == 0) 3288404aeb5SAlexandre Ganea NumThreads = std::min(hardware_concurrency().compute_thread_count(), 3298404aeb5SAlexandre Ganea unsigned((Inputs.size() + 1) / 2)); 3308404aeb5SAlexandre Ganea // FIXME: There's a bug here, where setting NumThreads = Inputs.size() fails 3318404aeb5SAlexandre Ganea // the merge_empty_profile.test because the InstrProfWriter.ProfileKind isn't 3328404aeb5SAlexandre Ganea // merged, thus the emitted file ends up with a PF_Unknown kind. 333e3a0bf50SVedant Kumar 334e3a0bf50SVedant Kumar // Initialize the writer contexts. 335e3a0bf50SVedant Kumar SmallVector<std::unique_ptr<WriterContext>, 4> Contexts; 336e3a0bf50SVedant Kumar for (unsigned I = 0; I < NumThreads; ++I) 3370eaee545SJonas Devlieghere Contexts.emplace_back(std::make_unique<WriterContext>( 338e3a0bf50SVedant Kumar OutputSparse, ErrorLock, WriterErrorCodes)); 339e3a0bf50SVedant Kumar 340e3a0bf50SVedant Kumar if (NumThreads == 1) { 341e3a0bf50SVedant Kumar for (const auto &Input : Inputs) 3423164fcfdSRichard Smith loadInput(Input, Remapper, Contexts[0].get()); 343e3a0bf50SVedant Kumar } else { 3448404aeb5SAlexandre Ganea ThreadPool Pool(hardware_concurrency(NumThreads)); 345e3a0bf50SVedant Kumar 346e3a0bf50SVedant Kumar // Load the inputs in parallel (N/NumThreads serial steps). 347e3a0bf50SVedant Kumar unsigned Ctx = 0; 3487f5b47ddSNathan Slingerland for (const auto &Input : Inputs) { 3493164fcfdSRichard Smith Pool.async(loadInput, Input, Remapper, Contexts[Ctx].get()); 350e3a0bf50SVedant Kumar Ctx = (Ctx + 1) % NumThreads; 351e3a0bf50SVedant Kumar } 352e3a0bf50SVedant Kumar Pool.wait(); 35321ab20e0SVedant Kumar 354e3a0bf50SVedant Kumar // Merge the writer contexts together (~ lg(NumThreads) serial steps). 355e3a0bf50SVedant Kumar unsigned Mid = Contexts.size() / 2; 356e3a0bf50SVedant Kumar unsigned End = Contexts.size(); 357e3a0bf50SVedant Kumar assert(Mid > 0 && "Expected more than one context"); 358e3a0bf50SVedant Kumar do { 359e3a0bf50SVedant Kumar for (unsigned I = 0; I < Mid; ++I) 360e3a0bf50SVedant Kumar Pool.async(mergeWriterContexts, Contexts[I].get(), 361e3a0bf50SVedant Kumar Contexts[I + Mid].get()); 362e3a0bf50SVedant Kumar Pool.wait(); 363e3a0bf50SVedant Kumar if (End & 1) { 364e3a0bf50SVedant Kumar Pool.async(mergeWriterContexts, Contexts[0].get(), 365e3a0bf50SVedant Kumar Contexts[End - 1].get()); 366e3a0bf50SVedant Kumar Pool.wait(); 367e3a0bf50SVedant Kumar } 368e3a0bf50SVedant Kumar End = Mid; 369e3a0bf50SVedant Kumar Mid /= 2; 370e3a0bf50SVedant Kumar } while (Mid > 0); 371e3a0bf50SVedant Kumar } 37221ab20e0SVedant Kumar 3730fcfe897SVedant Kumar // Handle deferred errors encountered during merging. If the number of errors 3740fcfe897SVedant Kumar // is equal to the number of inputs the merge failed. 3750fcfe897SVedant Kumar unsigned NumErrors = 0; 376188efda5SVedant Kumar for (std::unique_ptr<WriterContext> &WC : Contexts) { 3770fcfe897SVedant Kumar for (auto &ErrorPair : WC->Errors) { 3780fcfe897SVedant Kumar ++NumErrors; 3790fcfe897SVedant Kumar warn(toString(std::move(ErrorPair.first)), ErrorPair.second); 380188efda5SVedant Kumar } 3810fcfe897SVedant Kumar } 3820fcfe897SVedant Kumar if (NumErrors == Inputs.size() || 3830fcfe897SVedant Kumar (NumErrors > 0 && FailMode == failIfAnyAreInvalid)) 3840fcfe897SVedant Kumar exitWithError("No profiles could be merged."); 385188efda5SVedant Kumar 38678fe6a3eSWei Mi writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer); 387b5794ca9SVedant Kumar } 388d5336ae2SDiego Novillo 389a23f6234SWei Mi /// The profile entry for a function in instrumentation profile. 390a23f6234SWei Mi struct InstrProfileEntry { 391a23f6234SWei Mi uint64_t MaxCount = 0; 392a23f6234SWei Mi float ZeroCounterRatio = 0.0; 393a23f6234SWei Mi InstrProfRecord *ProfRecord; 394a23f6234SWei Mi InstrProfileEntry(InstrProfRecord *Record); 395a23f6234SWei Mi InstrProfileEntry() = default; 396a23f6234SWei Mi }; 397a23f6234SWei Mi 398a23f6234SWei Mi InstrProfileEntry::InstrProfileEntry(InstrProfRecord *Record) { 399a23f6234SWei Mi ProfRecord = Record; 400a23f6234SWei Mi uint64_t CntNum = Record->Counts.size(); 401a23f6234SWei Mi uint64_t ZeroCntNum = 0; 402a23f6234SWei Mi for (size_t I = 0; I < CntNum; ++I) { 403a23f6234SWei Mi MaxCount = std::max(MaxCount, Record->Counts[I]); 404a23f6234SWei Mi ZeroCntNum += !Record->Counts[I]; 405a23f6234SWei Mi } 406a23f6234SWei Mi ZeroCounterRatio = (float)ZeroCntNum / CntNum; 407a23f6234SWei Mi } 408a23f6234SWei Mi 409a23f6234SWei Mi /// Either set all the counters in the instr profile entry \p IFE to -1 410a23f6234SWei Mi /// in order to drop the profile or scale up the counters in \p IFP to 411a23f6234SWei Mi /// be above hot threshold. We use the ratio of zero counters in the 412a23f6234SWei Mi /// profile of a function to decide the profile is helpful or harmful 413a23f6234SWei Mi /// for performance, and to choose whether to scale up or drop it. 414a23f6234SWei Mi static void updateInstrProfileEntry(InstrProfileEntry &IFE, 415a23f6234SWei Mi uint64_t HotInstrThreshold, 416a23f6234SWei Mi float ZeroCounterThreshold) { 417a23f6234SWei Mi InstrProfRecord *ProfRecord = IFE.ProfRecord; 418a23f6234SWei Mi if (!IFE.MaxCount || IFE.ZeroCounterRatio > ZeroCounterThreshold) { 419a23f6234SWei Mi // If all or most of the counters of the function are zero, the 420a23f6234SWei Mi // profile is unaccountable and shuld be dropped. Reset all the 421a23f6234SWei Mi // counters to be -1 and PGO profile-use will drop the profile. 422a23f6234SWei Mi // All counters being -1 also implies that the function is hot so 423a23f6234SWei Mi // PGO profile-use will also set the entry count metadata to be 424a23f6234SWei Mi // above hot threshold. 425a23f6234SWei Mi for (size_t I = 0; I < ProfRecord->Counts.size(); ++I) 426a23f6234SWei Mi ProfRecord->Counts[I] = -1; 427a23f6234SWei Mi return; 428a23f6234SWei Mi } 429a23f6234SWei Mi 430a23f6234SWei Mi // Scale up the MaxCount to be multiple times above hot threshold. 431a23f6234SWei Mi const unsigned MultiplyFactor = 3; 432a23f6234SWei Mi uint64_t Numerator = HotInstrThreshold * MultiplyFactor; 433a23f6234SWei Mi uint64_t Denominator = IFE.MaxCount; 434a23f6234SWei Mi ProfRecord->scale(Numerator, Denominator, [&](instrprof_error E) { 435a23f6234SWei Mi warn(toString(make_error<InstrProfError>(E))); 436a23f6234SWei Mi }); 437a23f6234SWei Mi } 438a23f6234SWei Mi 439a23f6234SWei Mi const uint64_t ColdPercentileIdx = 15; 440a23f6234SWei Mi const uint64_t HotPercentileIdx = 11; 441a23f6234SWei Mi 442a23f6234SWei Mi /// Adjust the instr profile in \p WC based on the sample profile in 443a23f6234SWei Mi /// \p Reader. 444a23f6234SWei Mi static void 445a23f6234SWei Mi adjustInstrProfile(std::unique_ptr<WriterContext> &WC, 446a23f6234SWei Mi std::unique_ptr<sampleprof::SampleProfileReader> &Reader, 447a23f6234SWei Mi unsigned SupplMinSizeThreshold, float ZeroCounterThreshold, 448a23f6234SWei Mi unsigned InstrProfColdThreshold) { 449a23f6234SWei Mi // Function to its entry in instr profile. 450a23f6234SWei Mi StringMap<InstrProfileEntry> InstrProfileMap; 451a23f6234SWei Mi InstrProfSummaryBuilder IPBuilder(ProfileSummaryBuilder::DefaultCutoffs); 452a23f6234SWei Mi for (auto &PD : WC->Writer.getProfileData()) { 453a23f6234SWei Mi // Populate IPBuilder. 454a23f6234SWei Mi for (const auto &PDV : PD.getValue()) { 455a23f6234SWei Mi InstrProfRecord Record = PDV.second; 456a23f6234SWei Mi IPBuilder.addRecord(Record); 457a23f6234SWei Mi } 458a23f6234SWei Mi 459a23f6234SWei Mi // If a function has multiple entries in instr profile, skip it. 460a23f6234SWei Mi if (PD.getValue().size() != 1) 461a23f6234SWei Mi continue; 462a23f6234SWei Mi 463a23f6234SWei Mi // Initialize InstrProfileMap. 464a23f6234SWei Mi InstrProfRecord *R = &PD.getValue().begin()->second; 465a23f6234SWei Mi InstrProfileMap[PD.getKey()] = InstrProfileEntry(R); 466a23f6234SWei Mi } 467a23f6234SWei Mi 468a23f6234SWei Mi ProfileSummary InstrPS = *IPBuilder.getSummary(); 469a23f6234SWei Mi ProfileSummary SamplePS = Reader->getSummary(); 470a23f6234SWei Mi 471a23f6234SWei Mi // Compute cold thresholds for instr profile and sample profile. 472a23f6234SWei Mi uint64_t ColdSampleThreshold = 473a23f6234SWei Mi ProfileSummaryBuilder::getEntryForPercentile( 474a23f6234SWei Mi SamplePS.getDetailedSummary(), 475a23f6234SWei Mi ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx]) 476a23f6234SWei Mi .MinCount; 477a23f6234SWei Mi uint64_t HotInstrThreshold = 478a23f6234SWei Mi ProfileSummaryBuilder::getEntryForPercentile( 479a23f6234SWei Mi InstrPS.getDetailedSummary(), 480a23f6234SWei Mi ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx]) 481a23f6234SWei Mi .MinCount; 482a23f6234SWei Mi uint64_t ColdInstrThreshold = 483a23f6234SWei Mi InstrProfColdThreshold 484a23f6234SWei Mi ? InstrProfColdThreshold 485a23f6234SWei Mi : ProfileSummaryBuilder::getEntryForPercentile( 486a23f6234SWei Mi InstrPS.getDetailedSummary(), 487a23f6234SWei Mi ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx]) 488a23f6234SWei Mi .MinCount; 489a23f6234SWei Mi 490a23f6234SWei Mi // Find hot/warm functions in sample profile which is cold in instr profile 491a23f6234SWei Mi // and adjust the profiles of those functions in the instr profile. 492a23f6234SWei Mi for (const auto &PD : Reader->getProfiles()) { 493a23f6234SWei Mi StringRef FName = PD.getKey(); 494a23f6234SWei Mi const sampleprof::FunctionSamples &FS = PD.getValue(); 495a23f6234SWei Mi auto It = InstrProfileMap.find(FName); 496a23f6234SWei Mi if (FS.getHeadSamples() > ColdSampleThreshold && 497a23f6234SWei Mi It != InstrProfileMap.end() && 498a23f6234SWei Mi It->second.MaxCount <= ColdInstrThreshold && 499a23f6234SWei Mi FS.getBodySamples().size() >= SupplMinSizeThreshold) { 500a23f6234SWei Mi updateInstrProfileEntry(It->second, HotInstrThreshold, 501a23f6234SWei Mi ZeroCounterThreshold); 502a23f6234SWei Mi } 503a23f6234SWei Mi } 504a23f6234SWei Mi } 505a23f6234SWei Mi 506a23f6234SWei Mi /// The main function to supplement instr profile with sample profile. 507a23f6234SWei Mi /// \Inputs contains the instr profile. \p SampleFilename specifies the 508a23f6234SWei Mi /// sample profile. \p OutputFilename specifies the output profile name. 509a23f6234SWei Mi /// \p OutputFormat specifies the output profile format. \p OutputSparse 510a23f6234SWei Mi /// specifies whether to generate sparse profile. \p SupplMinSizeThreshold 511a23f6234SWei Mi /// specifies the minimal size for the functions whose profile will be 512a23f6234SWei Mi /// adjusted. \p ZeroCounterThreshold is the threshold to check whether 513a23f6234SWei Mi /// a function contains too many zero counters and whether its profile 514a23f6234SWei Mi /// should be dropped. \p InstrProfColdThreshold is the user specified 515a23f6234SWei Mi /// cold threshold which will override the cold threshold got from the 516a23f6234SWei Mi /// instr profile summary. 517a23f6234SWei Mi static void supplementInstrProfile( 518a23f6234SWei Mi const WeightedFileVector &Inputs, StringRef SampleFilename, 519a23f6234SWei Mi StringRef OutputFilename, ProfileFormat OutputFormat, bool OutputSparse, 520a23f6234SWei Mi unsigned SupplMinSizeThreshold, float ZeroCounterThreshold, 521a23f6234SWei Mi unsigned InstrProfColdThreshold) { 522a23f6234SWei Mi if (OutputFilename.compare("-") == 0) 523a23f6234SWei Mi exitWithError("Cannot write indexed profdata format to stdout."); 524a23f6234SWei Mi if (Inputs.size() != 1) 525a23f6234SWei Mi exitWithError("Expect one input to be an instr profile."); 526a23f6234SWei Mi if (Inputs[0].Weight != 1) 527a23f6234SWei Mi exitWithError("Expect instr profile doesn't have weight."); 528a23f6234SWei Mi 529a23f6234SWei Mi StringRef InstrFilename = Inputs[0].Filename; 530a23f6234SWei Mi 531a23f6234SWei Mi // Read sample profile. 532a23f6234SWei Mi LLVMContext Context; 533a23f6234SWei Mi auto ReaderOrErr = 534a23f6234SWei Mi sampleprof::SampleProfileReader::create(SampleFilename.str(), Context); 535a23f6234SWei Mi if (std::error_code EC = ReaderOrErr.getError()) 536a23f6234SWei Mi exitWithErrorCode(EC, SampleFilename); 537a23f6234SWei Mi auto Reader = std::move(ReaderOrErr.get()); 538a23f6234SWei Mi if (std::error_code EC = Reader->read()) 539a23f6234SWei Mi exitWithErrorCode(EC, SampleFilename); 540a23f6234SWei Mi 541a23f6234SWei Mi // Read instr profile. 542a23f6234SWei Mi std::mutex ErrorLock; 543a23f6234SWei Mi SmallSet<instrprof_error, 4> WriterErrorCodes; 544a23f6234SWei Mi auto WC = std::make_unique<WriterContext>(OutputSparse, ErrorLock, 545a23f6234SWei Mi WriterErrorCodes); 546a23f6234SWei Mi loadInput(Inputs[0], nullptr, WC.get()); 547a23f6234SWei Mi if (WC->Errors.size() > 0) 548a23f6234SWei Mi exitWithError(std::move(WC->Errors[0].first), InstrFilename); 549a23f6234SWei Mi 550a23f6234SWei Mi adjustInstrProfile(WC, Reader, SupplMinSizeThreshold, ZeroCounterThreshold, 551a23f6234SWei Mi InstrProfColdThreshold); 552a23f6234SWei Mi writeInstrProfile(OutputFilename, OutputFormat, WC->Writer); 553a23f6234SWei Mi } 554a23f6234SWei Mi 5553164fcfdSRichard Smith /// Make a copy of the given function samples with all symbol names remapped 5563164fcfdSRichard Smith /// by the provided symbol remapper. 5573164fcfdSRichard Smith static sampleprof::FunctionSamples 5583164fcfdSRichard Smith remapSamples(const sampleprof::FunctionSamples &Samples, 5593164fcfdSRichard Smith SymbolRemapper &Remapper, sampleprof_error &Error) { 5603164fcfdSRichard Smith sampleprof::FunctionSamples Result; 5613164fcfdSRichard Smith Result.setName(Remapper(Samples.getName())); 5623164fcfdSRichard Smith Result.addTotalSamples(Samples.getTotalSamples()); 5633164fcfdSRichard Smith Result.addHeadSamples(Samples.getHeadSamples()); 5643164fcfdSRichard Smith for (const auto &BodySample : Samples.getBodySamples()) { 5653164fcfdSRichard Smith Result.addBodySamples(BodySample.first.LineOffset, 5663164fcfdSRichard Smith BodySample.first.Discriminator, 5673164fcfdSRichard Smith BodySample.second.getSamples()); 5683164fcfdSRichard Smith for (const auto &Target : BodySample.second.getCallTargets()) { 5693164fcfdSRichard Smith Result.addCalledTargetSamples(BodySample.first.LineOffset, 5703164fcfdSRichard Smith BodySample.first.Discriminator, 5713164fcfdSRichard Smith Remapper(Target.first()), Target.second); 5723164fcfdSRichard Smith } 5733164fcfdSRichard Smith } 5743164fcfdSRichard Smith for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) { 5753164fcfdSRichard Smith sampleprof::FunctionSamplesMap &Target = 5763164fcfdSRichard Smith Result.functionSamplesAt(CallsiteSamples.first); 5773164fcfdSRichard Smith for (const auto &Callsite : CallsiteSamples.second) { 5783164fcfdSRichard Smith sampleprof::FunctionSamples Remapped = 5793164fcfdSRichard Smith remapSamples(Callsite.second, Remapper, Error); 580adcd0268SBenjamin Kramer MergeResult(Error, 581adcd0268SBenjamin Kramer Target[std::string(Remapped.getName())].merge(Remapped)); 5823164fcfdSRichard Smith } 5833164fcfdSRichard Smith } 5843164fcfdSRichard Smith return Result; 5853164fcfdSRichard Smith } 5863164fcfdSRichard Smith 5876f7c19a4SXinliang David Li static sampleprof::SampleProfileFormat FormatMap[] = { 588be907324SWei Mi sampleprof::SPF_None, 589be907324SWei Mi sampleprof::SPF_Text, 590be907324SWei Mi sampleprof::SPF_Compact_Binary, 591be907324SWei Mi sampleprof::SPF_Ext_Binary, 592be907324SWei Mi sampleprof::SPF_GCC, 593be907324SWei Mi sampleprof::SPF_Binary}; 5946f7c19a4SXinliang David Li 595798e59b8SWei Mi static std::unique_ptr<MemoryBuffer> 596798e59b8SWei Mi getInputFileBuf(const StringRef &InputFile) { 597798e59b8SWei Mi if (InputFile == "") 598798e59b8SWei Mi return {}; 599798e59b8SWei Mi 600798e59b8SWei Mi auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile); 601798e59b8SWei Mi if (!BufOrError) 602798e59b8SWei Mi exitWithErrorCode(BufOrError.getError(), InputFile); 603798e59b8SWei Mi 604798e59b8SWei Mi return std::move(*BufOrError); 605798e59b8SWei Mi } 606798e59b8SWei Mi 607798e59b8SWei Mi static void populateProfileSymbolList(MemoryBuffer *Buffer, 608798e59b8SWei Mi sampleprof::ProfileSymbolList &PSL) { 609798e59b8SWei Mi if (!Buffer) 610798e59b8SWei Mi return; 611798e59b8SWei Mi 612798e59b8SWei Mi SmallVector<StringRef, 32> SymbolVec; 613798e59b8SWei Mi StringRef Data = Buffer->getBuffer(); 614798e59b8SWei Mi Data.split(SymbolVec, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); 615798e59b8SWei Mi 616798e59b8SWei Mi for (StringRef symbol : SymbolVec) 617798e59b8SWei Mi PSL.add(symbol); 618798e59b8SWei Mi } 619798e59b8SWei Mi 620b523790aSWei Mi static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer, 621b523790aSWei Mi ProfileFormat OutputFormat, 622b523790aSWei Mi MemoryBuffer *Buffer, 623b523790aSWei Mi sampleprof::ProfileSymbolList &WriterList, 624b49eac71SWei Mi bool CompressAllSections, bool UseMD5, 62556926ae0SWei Mi bool GenPartialProfile) { 626b523790aSWei Mi populateProfileSymbolList(Buffer, WriterList); 627b523790aSWei Mi if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary) 628b523790aSWei Mi warn("Profile Symbol list is not empty but the output format is not " 629b523790aSWei Mi "ExtBinary format. The list will be lost in the output. "); 630b523790aSWei Mi 631b523790aSWei Mi Writer.setProfileSymbolList(&WriterList); 632b523790aSWei Mi 633b523790aSWei Mi if (CompressAllSections) { 634ebad6788SWei Mi if (OutputFormat != PF_Ext_Binary) 635b523790aSWei Mi warn("-compress-all-section is ignored. Specify -extbinary to enable it"); 636ebad6788SWei Mi else 637ebad6788SWei Mi Writer.setToCompressAllSections(); 638b523790aSWei Mi } 639ebad6788SWei Mi if (UseMD5) { 640ebad6788SWei Mi if (OutputFormat != PF_Ext_Binary) 641ebad6788SWei Mi warn("-use-md5 is ignored. Specify -extbinary to enable it"); 642ebad6788SWei Mi else 643ebad6788SWei Mi Writer.setUseMD5(); 644b523790aSWei Mi } 64556926ae0SWei Mi if (GenPartialProfile) { 646b49eac71SWei Mi if (OutputFormat != PF_Ext_Binary) 64756926ae0SWei Mi warn("-gen-partial-profile is ignored. Specify -extbinary to enable it"); 648b49eac71SWei Mi else 649b49eac71SWei Mi Writer.setPartialProfile(); 650b49eac71SWei Mi } 651b523790aSWei Mi } 652b523790aSWei Mi 653ebad6788SWei Mi static void 654ebad6788SWei Mi mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, 655ebad6788SWei Mi StringRef OutputFilename, ProfileFormat OutputFormat, 656ebad6788SWei Mi StringRef ProfileSymbolListFile, bool CompressAllSections, 65756926ae0SWei Mi bool UseMD5, bool GenPartialProfile, FailureMode FailMode) { 658d5336ae2SDiego Novillo using namespace sampleprof; 659d5336ae2SDiego Novillo StringMap<FunctionSamples> ProfileMap; 660aae1ed8eSDiego Novillo SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers; 66103b42e41SMehdi Amini LLVMContext Context; 662798e59b8SWei Mi sampleprof::ProfileSymbolList WriterList; 6637f5b47ddSNathan Slingerland for (const auto &Input : Inputs) { 66403b42e41SMehdi Amini auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context); 6650fcfe897SVedant Kumar if (std::error_code EC = ReaderOrErr.getError()) { 6660fcfe897SVedant Kumar warnOrExitGivenError(FailMode, EC, Input.Filename); 6670fcfe897SVedant Kumar continue; 6680fcfe897SVedant Kumar } 669d5336ae2SDiego Novillo 670aae1ed8eSDiego Novillo // We need to keep the readers around until after all the files are 671aae1ed8eSDiego Novillo // read so that we do not lose the function names stored in each 672aae1ed8eSDiego Novillo // reader's memory. The function names are needed to write out the 673aae1ed8eSDiego Novillo // merged profile map. 674aae1ed8eSDiego Novillo Readers.push_back(std::move(ReaderOrErr.get())); 675aae1ed8eSDiego Novillo const auto Reader = Readers.back().get(); 6760fcfe897SVedant Kumar if (std::error_code EC = Reader->read()) { 6770fcfe897SVedant Kumar warnOrExitGivenError(FailMode, EC, Input.Filename); 6780fcfe897SVedant Kumar Readers.pop_back(); 6790fcfe897SVedant Kumar continue; 6800fcfe897SVedant Kumar } 681d5336ae2SDiego Novillo 682d5336ae2SDiego Novillo StringMap<FunctionSamples> &Profiles = Reader->getProfiles(); 683d5336ae2SDiego Novillo for (StringMap<FunctionSamples>::iterator I = Profiles.begin(), 684d5336ae2SDiego Novillo E = Profiles.end(); 685d5336ae2SDiego Novillo I != E; ++I) { 6863164fcfdSRichard Smith sampleprof_error Result = sampleprof_error::success; 6873164fcfdSRichard Smith FunctionSamples Remapped = 6883164fcfdSRichard Smith Remapper ? remapSamples(I->second, *Remapper, Result) 6893164fcfdSRichard Smith : FunctionSamples(); 6903164fcfdSRichard Smith FunctionSamples &Samples = Remapper ? Remapped : I->second; 6913164fcfdSRichard Smith StringRef FName = Samples.getName(); 6923164fcfdSRichard Smith MergeResult(Result, ProfileMap[FName].merge(Samples, Input.Weight)); 69348dd080cSNathan Slingerland if (Result != sampleprof_error::success) { 69448dd080cSNathan Slingerland std::error_code EC = make_error_code(Result); 6959152fd17SVedant Kumar handleMergeWriterError(errorCodeToError(EC), Input.Filename, FName); 69648dd080cSNathan Slingerland } 697d5336ae2SDiego Novillo } 698798e59b8SWei Mi 699798e59b8SWei Mi std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList = 700798e59b8SWei Mi Reader->getProfileSymbolList(); 701798e59b8SWei Mi if (ReaderList) 702798e59b8SWei Mi WriterList.merge(*ReaderList); 703d5336ae2SDiego Novillo } 704f0d3dcecSRong Xu auto WriterOrErr = 705f0d3dcecSRong Xu SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]); 706f0d3dcecSRong Xu if (std::error_code EC = WriterOrErr.getError()) 707f0d3dcecSRong Xu exitWithErrorCode(EC, OutputFilename); 708f0d3dcecSRong Xu 709b523790aSWei Mi auto Writer = std::move(WriterOrErr.get()); 710798e59b8SWei Mi // WriterList will have StringRef refering to string in Buffer. 711798e59b8SWei Mi // Make sure Buffer lives as long as WriterList. 712798e59b8SWei Mi auto Buffer = getInputFileBuf(ProfileSymbolListFile); 713b523790aSWei Mi handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList, 71456926ae0SWei Mi CompressAllSections, UseMD5, GenPartialProfile); 715d5336ae2SDiego Novillo Writer->write(ProfileMap); 716d5336ae2SDiego Novillo } 717d5336ae2SDiego Novillo 7187f5b47ddSNathan Slingerland static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) { 7198d0e861eSVedant Kumar StringRef WeightStr, FileName; 7208d0e861eSVedant Kumar std::tie(WeightStr, FileName) = WeightedFilename.split(','); 721d5336ae2SDiego Novillo 7227f5b47ddSNathan Slingerland uint64_t Weight; 7237f5b47ddSNathan Slingerland if (WeightStr.getAsInteger(10, Weight) || Weight < 1) 7247f5b47ddSNathan Slingerland exitWithError("Input weight must be a positive integer."); 7257f5b47ddSNathan Slingerland 726adcd0268SBenjamin Kramer return {std::string(FileName), Weight}; 7277f5b47ddSNathan Slingerland } 7287f5b47ddSNathan Slingerland 7299a1bfcfaSXinliang David Li static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) { 7309a1bfcfaSXinliang David Li StringRef Filename = WF.Filename; 7319a1bfcfaSXinliang David Li uint64_t Weight = WF.Weight; 732a81f4728SBenjamin Kramer 733a81f4728SBenjamin Kramer // If it's STDIN just pass it on. 734a81f4728SBenjamin Kramer if (Filename == "-") { 735adcd0268SBenjamin Kramer WNI.push_back({std::string(Filename), Weight}); 736a81f4728SBenjamin Kramer return; 737a81f4728SBenjamin Kramer } 738a81f4728SBenjamin Kramer 7399a1bfcfaSXinliang David Li llvm::sys::fs::file_status Status; 7409a1bfcfaSXinliang David Li llvm::sys::fs::status(Filename, Status); 7419a1bfcfaSXinliang David Li if (!llvm::sys::fs::exists(Status)) 7429a1bfcfaSXinliang David Li exitWithErrorCode(make_error_code(errc::no_such_file_or_directory), 7439a1bfcfaSXinliang David Li Filename); 7449a1bfcfaSXinliang David Li // If it's a source file, collect it. 7459a1bfcfaSXinliang David Li if (llvm::sys::fs::is_regular_file(Status)) { 746adcd0268SBenjamin Kramer WNI.push_back({std::string(Filename), Weight}); 7479a1bfcfaSXinliang David Li return; 7489a1bfcfaSXinliang David Li } 7499a1bfcfaSXinliang David Li 7509a1bfcfaSXinliang David Li if (llvm::sys::fs::is_directory(Status)) { 7519a1bfcfaSXinliang David Li std::error_code EC; 7529a1bfcfaSXinliang David Li for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E; 7539a1bfcfaSXinliang David Li F != E && !EC; F.increment(EC)) { 7549a1bfcfaSXinliang David Li if (llvm::sys::fs::is_regular_file(F->path())) { 7559a1bfcfaSXinliang David Li addWeightedInput(WNI, {F->path(), Weight}); 7569a1bfcfaSXinliang David Li } 7579a1bfcfaSXinliang David Li } 7589a1bfcfaSXinliang David Li if (EC) 7599a1bfcfaSXinliang David Li exitWithErrorCode(EC, Filename); 7609a1bfcfaSXinliang David Li } 7619a1bfcfaSXinliang David Li } 7629a1bfcfaSXinliang David Li 763cef4360aSVedant Kumar static void parseInputFilenamesFile(MemoryBuffer *Buffer, 764cef4360aSVedant Kumar WeightedFileVector &WFV) { 765cef4360aSVedant Kumar if (!Buffer) 766cef4360aSVedant Kumar return; 767cef4360aSVedant Kumar 768cef4360aSVedant Kumar SmallVector<StringRef, 8> Entries; 769cef4360aSVedant Kumar StringRef Data = Buffer->getBuffer(); 770cef4360aSVedant Kumar Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); 771cef4360aSVedant Kumar for (const StringRef &FileWeightEntry : Entries) { 772cef4360aSVedant Kumar StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r"); 773cef4360aSVedant Kumar // Skip comments. 774cef4360aSVedant Kumar if (SanitizedEntry.startswith("#")) 775cef4360aSVedant Kumar continue; 776cef4360aSVedant Kumar // If there's no comma, it's an unweighted profile. 777cef4360aSVedant Kumar else if (SanitizedEntry.find(',') == StringRef::npos) 778adcd0268SBenjamin Kramer addWeightedInput(WFV, {std::string(SanitizedEntry), 1}); 779cef4360aSVedant Kumar else 7809a1bfcfaSXinliang David Li addWeightedInput(WFV, parseWeightedFile(SanitizedEntry)); 781cef4360aSVedant Kumar } 782cef4360aSVedant Kumar } 783cef4360aSVedant Kumar 7847f5b47ddSNathan Slingerland static int merge_main(int argc, const char *argv[]) { 7857f5b47ddSNathan Slingerland cl::list<std::string> InputFilenames(cl::Positional, 7867f5b47ddSNathan Slingerland cl::desc("<filename...>")); 7877f5b47ddSNathan Slingerland cl::list<std::string> WeightedInputFilenames("weighted-input", 7887f5b47ddSNathan Slingerland cl::desc("<weight>,<filename>")); 789cef4360aSVedant Kumar cl::opt<std::string> InputFilenamesFile( 790cef4360aSVedant Kumar "input-files", cl::init(""), 791cef4360aSVedant Kumar cl::desc("Path to file containing newline-separated " 792cef4360aSVedant Kumar "[<weight>,]<filename> entries")); 793cef4360aSVedant Kumar cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"), 794cef4360aSVedant Kumar cl::aliasopt(InputFilenamesFile)); 795cef4360aSVedant Kumar cl::opt<bool> DumpInputFileList( 796cef4360aSVedant Kumar "dump-input-file-list", cl::init(false), cl::Hidden, 797cef4360aSVedant Kumar cl::desc("Dump the list of input files and their weights, then exit")); 7983164fcfdSRichard Smith cl::opt<std::string> RemappingFile("remapping-file", cl::value_desc("file"), 7993164fcfdSRichard Smith cl::desc("Symbol remapping file")); 8003164fcfdSRichard Smith cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"), 8013164fcfdSRichard Smith cl::aliasopt(RemappingFile)); 802d5336ae2SDiego Novillo cl::opt<std::string> OutputFilename("output", cl::value_desc("output"), 803d5336ae2SDiego Novillo cl::init("-"), cl::Required, 804d5336ae2SDiego Novillo cl::desc("Output file")); 805d5336ae2SDiego Novillo cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), 806d5336ae2SDiego Novillo cl::aliasopt(OutputFilename)); 807d5336ae2SDiego Novillo cl::opt<ProfileKinds> ProfileKind( 808d5336ae2SDiego Novillo cl::desc("Profile kind:"), cl::init(instr), 809d5336ae2SDiego Novillo cl::values(clEnumVal(instr, "Instrumentation profile (default)"), 810732afdd0SMehdi Amini clEnumVal(sample, "Sample profile"))); 8116f7c19a4SXinliang David Li cl::opt<ProfileFormat> OutputFormat( 812d9be2c7eSWei Mi cl::desc("Format of output profile"), cl::init(PF_Binary), 813be907324SWei Mi cl::values( 814be907324SWei Mi clEnumValN(PF_Binary, "binary", "Binary encoding (default)"), 815a0c0857eSWei Mi clEnumValN(PF_Compact_Binary, "compbinary", 816432db3b4SWei Mi "Compact binary encoding"), 817be907324SWei Mi clEnumValN(PF_Ext_Binary, "extbinary", "Extensible binary encoding"), 8186f7c19a4SXinliang David Li clEnumValN(PF_Text, "text", "Text encoding"), 8196f7c19a4SXinliang David Li clEnumValN(PF_GCC, "gcc", 820732afdd0SMehdi Amini "GCC encoding (only meaningful for -sample)"))); 8210fcfe897SVedant Kumar cl::opt<FailureMode> FailureMode( 8220fcfe897SVedant Kumar "failure-mode", cl::init(failIfAnyAreInvalid), cl::desc("Failure mode:"), 8230fcfe897SVedant Kumar cl::values(clEnumValN(failIfAnyAreInvalid, "any", 8240fcfe897SVedant Kumar "Fail if any profile is invalid."), 8250fcfe897SVedant Kumar clEnumValN(failIfAllAreInvalid, "all", 8260fcfe897SVedant Kumar "Fail only if all profiles are invalid."))); 82700dab228SVedant Kumar cl::opt<bool> OutputSparse("sparse", cl::init(false), 82800dab228SVedant Kumar cl::desc("Generate a sparse profile (only meaningful for -instr)")); 829e3a0bf50SVedant Kumar cl::opt<unsigned> NumThreads( 830e3a0bf50SVedant Kumar "num-threads", cl::init(0), 831e3a0bf50SVedant Kumar cl::desc("Number of merge threads to use (default: autodetect)")); 832e3a0bf50SVedant Kumar cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), 833e3a0bf50SVedant Kumar cl::aliasopt(NumThreads)); 834798e59b8SWei Mi cl::opt<std::string> ProfileSymbolListFile( 835798e59b8SWei Mi "prof-sym-list", cl::init(""), 836798e59b8SWei Mi cl::desc("Path to file containing the list of function symbols " 837798e59b8SWei Mi "used to populate profile symbol list")); 838b523790aSWei Mi cl::opt<bool> CompressAllSections( 839b523790aSWei Mi "compress-all-sections", cl::init(false), cl::Hidden, 840b523790aSWei Mi cl::desc("Compress all sections when writing the profile (only " 841b523790aSWei Mi "meaningful for -extbinary)")); 842ebad6788SWei Mi cl::opt<bool> UseMD5( 843ebad6788SWei Mi "use-md5", cl::init(false), cl::Hidden, 844ebad6788SWei Mi cl::desc("Choose to use MD5 to represent string in name table (only " 845ebad6788SWei Mi "meaningful for -extbinary)")); 84656926ae0SWei Mi cl::opt<bool> GenPartialProfile( 84756926ae0SWei Mi "gen-partial-profile", cl::init(false), cl::Hidden, 84856926ae0SWei Mi cl::desc("Generate a partial profile (only meaningful for -extbinary)")); 849a23f6234SWei Mi cl::opt<std::string> SupplInstrWithSample( 850a23f6234SWei Mi "supplement-instr-with-sample", cl::init(""), cl::Hidden, 851a23f6234SWei Mi cl::desc("Supplement an instr profile with sample profile, to correct " 852a23f6234SWei Mi "the profile unrepresentativeness issue. The sample " 853a23f6234SWei Mi "profile is the input of the flag. Output will be in instr " 854a23f6234SWei Mi "format (The flag only works with -instr)")); 855a23f6234SWei Mi cl::opt<float> ZeroCounterThreshold( 856a23f6234SWei Mi "zero-counter-threshold", cl::init(0.7), cl::Hidden, 857a23f6234SWei Mi cl::desc("For the function which is cold in instr profile but hot in " 858a23f6234SWei Mi "sample profile, if the ratio of the number of zero counters " 859a23f6234SWei Mi "divided by the the total number of counters is above the " 860a23f6234SWei Mi "threshold, the profile of the function will be regarded as " 861a23f6234SWei Mi "being harmful for performance and will be dropped. ")); 862a23f6234SWei Mi cl::opt<unsigned> SupplMinSizeThreshold( 863a23f6234SWei Mi "suppl-min-size-threshold", cl::init(10), cl::Hidden, 864a23f6234SWei Mi cl::desc("If the size of a function is smaller than the threshold, " 865a23f6234SWei Mi "assume it can be inlined by PGO early inliner and it won't " 866a23f6234SWei Mi "be adjusted based on sample profile. ")); 867a23f6234SWei Mi cl::opt<unsigned> InstrProfColdThreshold( 868a23f6234SWei Mi "instr-prof-cold-threshold", cl::init(0), cl::Hidden, 869a23f6234SWei Mi cl::desc("User specified cold threshold for instr profile which will " 870a23f6234SWei Mi "override the cold threshold got from profile summary. ")); 87100dab228SVedant Kumar 872d5336ae2SDiego Novillo cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); 873d5336ae2SDiego Novillo 874cef4360aSVedant Kumar WeightedFileVector WeightedInputs; 875cef4360aSVedant Kumar for (StringRef Filename : InputFilenames) 876adcd0268SBenjamin Kramer addWeightedInput(WeightedInputs, {std::string(Filename), 1}); 877cef4360aSVedant Kumar for (StringRef WeightedFilename : WeightedInputFilenames) 8789a1bfcfaSXinliang David Li addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename)); 879cef4360aSVedant Kumar 880cef4360aSVedant Kumar // Make sure that the file buffer stays alive for the duration of the 881cef4360aSVedant Kumar // weighted input vector's lifetime. 882798e59b8SWei Mi auto Buffer = getInputFileBuf(InputFilenamesFile); 883cef4360aSVedant Kumar parseInputFilenamesFile(Buffer.get(), WeightedInputs); 884cef4360aSVedant Kumar 885cef4360aSVedant Kumar if (WeightedInputs.empty()) 8860c30f89cSChandler Carruth exitWithError("No input files specified. See " + 8870c30f89cSChandler Carruth sys::path::filename(argv[0]) + " -help"); 8880c30f89cSChandler Carruth 889cef4360aSVedant Kumar if (DumpInputFileList) { 890cef4360aSVedant Kumar for (auto &WF : WeightedInputs) 891cef4360aSVedant Kumar outs() << WF.Weight << "," << WF.Filename << "\n"; 892cef4360aSVedant Kumar return 0; 893cef4360aSVedant Kumar } 894f771a050SVedant Kumar 8953164fcfdSRichard Smith std::unique_ptr<SymbolRemapper> Remapper; 8963164fcfdSRichard Smith if (!RemappingFile.empty()) 8973164fcfdSRichard Smith Remapper = SymbolRemapper::create(RemappingFile); 8983164fcfdSRichard Smith 899a23f6234SWei Mi if (!SupplInstrWithSample.empty()) { 900a23f6234SWei Mi if (ProfileKind != instr) 901a23f6234SWei Mi exitWithError( 902a23f6234SWei Mi "-supplement-instr-with-sample can only work with -instr. "); 903a23f6234SWei Mi 904a23f6234SWei Mi supplementInstrProfile(WeightedInputs, SupplInstrWithSample, OutputFilename, 905a23f6234SWei Mi OutputFormat, OutputSparse, SupplMinSizeThreshold, 906a23f6234SWei Mi ZeroCounterThreshold, InstrProfColdThreshold); 907a23f6234SWei Mi return 0; 908a23f6234SWei Mi } 909a23f6234SWei Mi 910d5336ae2SDiego Novillo if (ProfileKind == instr) 9113164fcfdSRichard Smith mergeInstrProfile(WeightedInputs, Remapper.get(), OutputFilename, 9120fcfe897SVedant Kumar OutputFormat, OutputSparse, NumThreads, FailureMode); 913d5336ae2SDiego Novillo else 9143164fcfdSRichard Smith mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename, 915b523790aSWei Mi OutputFormat, ProfileSymbolListFile, CompressAllSections, 91656926ae0SWei Mi UseMD5, GenPartialProfile, FailureMode); 917ec49f982SJustin Bogner 918bfee8d49SJustin Bogner return 0; 919bfee8d49SJustin Bogner } 920618bcea7SJustin Bogner 921998b97f6SRong Xu /// Computer the overlap b/w profile BaseFilename and profile TestFilename. 922998b97f6SRong Xu static void overlapInstrProfile(const std::string &BaseFilename, 923998b97f6SRong Xu const std::string &TestFilename, 924998b97f6SRong Xu const OverlapFuncFilters &FuncFilter, 925998b97f6SRong Xu raw_fd_ostream &OS, bool IsCS) { 926998b97f6SRong Xu std::mutex ErrorLock; 927998b97f6SRong Xu SmallSet<instrprof_error, 4> WriterErrorCodes; 928998b97f6SRong Xu WriterContext Context(false, ErrorLock, WriterErrorCodes); 929998b97f6SRong Xu WeightedFile WeightedInput{BaseFilename, 1}; 930998b97f6SRong Xu OverlapStats Overlap; 931e0fa2689SRong Xu Error E = Overlap.accumulateCounts(BaseFilename, TestFilename, IsCS); 932998b97f6SRong Xu if (E) 933998b97f6SRong Xu exitWithError(std::move(E), "Error in getting profile count sums"); 934998b97f6SRong Xu if (Overlap.Base.CountSum < 1.0f) { 935998b97f6SRong Xu OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n"; 936998b97f6SRong Xu exit(0); 937998b97f6SRong Xu } 938998b97f6SRong Xu if (Overlap.Test.CountSum < 1.0f) { 939998b97f6SRong Xu OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n"; 940998b97f6SRong Xu exit(0); 941998b97f6SRong Xu } 942998b97f6SRong Xu loadInput(WeightedInput, nullptr, &Context); 943998b97f6SRong Xu overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS, 944998b97f6SRong Xu IsCS); 945998b97f6SRong Xu Overlap.dump(OS); 946998b97f6SRong Xu } 947998b97f6SRong Xu 948*540489deSweihe namespace { 949*540489deSweihe struct SampleOverlapStats { 950*540489deSweihe StringRef BaseName; 951*540489deSweihe StringRef TestName; 952*540489deSweihe // Number of overlap units 953*540489deSweihe uint64_t OverlapCount; 954*540489deSweihe // Total samples of overlap units 955*540489deSweihe uint64_t OverlapSample; 956*540489deSweihe // Number of and total samples of units that only present in base or test 957*540489deSweihe // profile 958*540489deSweihe uint64_t BaseUniqueCount; 959*540489deSweihe uint64_t BaseUniqueSample; 960*540489deSweihe uint64_t TestUniqueCount; 961*540489deSweihe uint64_t TestUniqueSample; 962*540489deSweihe // Number of units and total samples in base or test profile 963*540489deSweihe uint64_t BaseCount; 964*540489deSweihe uint64_t BaseSample; 965*540489deSweihe uint64_t TestCount; 966*540489deSweihe uint64_t TestSample; 967*540489deSweihe // Number of and total samples of units that present in at least one profile 968*540489deSweihe uint64_t UnionCount; 969*540489deSweihe uint64_t UnionSample; 970*540489deSweihe // Weighted similarity 971*540489deSweihe double Similarity; 972*540489deSweihe // For SampleOverlapStats instances representing functions, weights of the 973*540489deSweihe // function in base and test profiles 974*540489deSweihe double BaseWeight; 975*540489deSweihe double TestWeight; 976*540489deSweihe 977*540489deSweihe SampleOverlapStats() 978*540489deSweihe : OverlapCount(0), OverlapSample(0), BaseUniqueCount(0), 979*540489deSweihe BaseUniqueSample(0), TestUniqueCount(0), TestUniqueSample(0), 980*540489deSweihe BaseCount(0), BaseSample(0), TestCount(0), TestSample(0), UnionCount(0), 981*540489deSweihe UnionSample(0), Similarity(0.0), BaseWeight(0.0), TestWeight(0.0) {} 982*540489deSweihe }; 983*540489deSweihe } // end anonymous namespace 984*540489deSweihe 985*540489deSweihe namespace { 986*540489deSweihe struct FuncSampleStats { 987*540489deSweihe uint64_t SampleSum; 988*540489deSweihe uint64_t MaxSample; 989*540489deSweihe uint64_t HotBlockCount; 990*540489deSweihe FuncSampleStats() : SampleSum(0), MaxSample(0), HotBlockCount(0) {} 991*540489deSweihe FuncSampleStats(uint64_t SampleSum, uint64_t MaxSample, 992*540489deSweihe uint64_t HotBlockCount) 993*540489deSweihe : SampleSum(SampleSum), MaxSample(MaxSample), 994*540489deSweihe HotBlockCount(HotBlockCount) {} 995*540489deSweihe }; 996*540489deSweihe } // end anonymous namespace 997*540489deSweihe 998*540489deSweihe namespace { 999*540489deSweihe enum MatchStatus { MS_Match, MS_FirstUnique, MS_SecondUnique, MS_None }; 1000*540489deSweihe 1001*540489deSweihe // Class for updating merging steps for two sorted maps. The class should be 1002*540489deSweihe // instantiated with a map iterator type. 1003*540489deSweihe template <class T> class MatchStep { 1004*540489deSweihe public: 1005*540489deSweihe MatchStep() = delete; 1006*540489deSweihe 1007*540489deSweihe MatchStep(T FirstIter, T FirstEnd, T SecondIter, T SecondEnd) 1008*540489deSweihe : FirstIter(FirstIter), FirstEnd(FirstEnd), SecondIter(SecondIter), 1009*540489deSweihe SecondEnd(SecondEnd), Status(MS_None) {} 1010*540489deSweihe 1011*540489deSweihe bool areBothFinished() const { 1012*540489deSweihe return (FirstIter == FirstEnd && SecondIter == SecondEnd); 1013*540489deSweihe } 1014*540489deSweihe 1015*540489deSweihe bool isFirstFinished() const { return FirstIter == FirstEnd; } 1016*540489deSweihe 1017*540489deSweihe bool isSecondFinished() const { return SecondIter == SecondEnd; } 1018*540489deSweihe 1019*540489deSweihe /// Advance one step based on the previous match status unless the previous 1020*540489deSweihe /// status is MS_None. Then update Status based on the comparison between two 1021*540489deSweihe /// container iterators at the current step. If the previous status is 1022*540489deSweihe /// MS_None, it means two iterators are at the beginning and no comparison has 1023*540489deSweihe /// been made, so we simply update Status without advancing the iterators. 1024*540489deSweihe void updateOneStep(); 1025*540489deSweihe 1026*540489deSweihe T getFirstIter() const { return FirstIter; } 1027*540489deSweihe 1028*540489deSweihe T getSecondIter() const { return SecondIter; } 1029*540489deSweihe 1030*540489deSweihe MatchStatus getMatchStatus() const { return Status; } 1031*540489deSweihe 1032*540489deSweihe private: 1033*540489deSweihe // Current iterator and end iterator of the first container. 1034*540489deSweihe T FirstIter; 1035*540489deSweihe T FirstEnd; 1036*540489deSweihe // Current iterator and end iterator of the second container. 1037*540489deSweihe T SecondIter; 1038*540489deSweihe T SecondEnd; 1039*540489deSweihe // Match status of the current step. 1040*540489deSweihe MatchStatus Status; 1041*540489deSweihe }; 1042*540489deSweihe } // end anonymous namespace 1043*540489deSweihe 1044*540489deSweihe template <class T> void MatchStep<T>::updateOneStep() { 1045*540489deSweihe switch (Status) { 1046*540489deSweihe case MS_Match: 1047*540489deSweihe ++FirstIter; 1048*540489deSweihe ++SecondIter; 1049*540489deSweihe break; 1050*540489deSweihe case MS_FirstUnique: 1051*540489deSweihe ++FirstIter; 1052*540489deSweihe break; 1053*540489deSweihe case MS_SecondUnique: 1054*540489deSweihe ++SecondIter; 1055*540489deSweihe break; 1056*540489deSweihe case MS_None: 1057*540489deSweihe break; 1058*540489deSweihe } 1059*540489deSweihe 1060*540489deSweihe // Update Status according to iterators at the current step. 1061*540489deSweihe if (areBothFinished()) 1062*540489deSweihe return; 1063*540489deSweihe if (FirstIter != FirstEnd && 1064*540489deSweihe (SecondIter == SecondEnd || FirstIter->first < SecondIter->first)) 1065*540489deSweihe Status = MS_FirstUnique; 1066*540489deSweihe else if (SecondIter != SecondEnd && 1067*540489deSweihe (FirstIter == FirstEnd || SecondIter->first < FirstIter->first)) 1068*540489deSweihe Status = MS_SecondUnique; 1069*540489deSweihe else 1070*540489deSweihe Status = MS_Match; 1071*540489deSweihe } 1072*540489deSweihe 1073*540489deSweihe // Return the sum of line/block samples, the max line/block sample, and the 1074*540489deSweihe // number of line/block samples above the given threshold in a function 1075*540489deSweihe // including its inlinees. 1076*540489deSweihe static void getFuncSampleStats(const sampleprof::FunctionSamples &Func, 1077*540489deSweihe FuncSampleStats &FuncStats, 1078*540489deSweihe uint64_t HotThreshold) { 1079*540489deSweihe for (const auto &L : Func.getBodySamples()) { 1080*540489deSweihe uint64_t Sample = L.second.getSamples(); 1081*540489deSweihe FuncStats.SampleSum += Sample; 1082*540489deSweihe FuncStats.MaxSample = std::max(FuncStats.MaxSample, Sample); 1083*540489deSweihe if (Sample >= HotThreshold) 1084*540489deSweihe ++FuncStats.HotBlockCount; 1085*540489deSweihe } 1086*540489deSweihe 1087*540489deSweihe for (const auto &C : Func.getCallsiteSamples()) { 1088*540489deSweihe for (const auto &F : C.second) 1089*540489deSweihe getFuncSampleStats(F.second, FuncStats, HotThreshold); 1090*540489deSweihe } 1091*540489deSweihe } 1092*540489deSweihe 1093*540489deSweihe /// Predicate that determines if a function is hot with a given threshold. We 1094*540489deSweihe /// keep it separate from its callsites for possible extension in the future. 1095*540489deSweihe static bool isFunctionHot(const FuncSampleStats &FuncStats, 1096*540489deSweihe uint64_t HotThreshold) { 1097*540489deSweihe // We intentionally compare the maximum sample count in a function with the 1098*540489deSweihe // HotThreshold to get an approximate determination on hot functions. 1099*540489deSweihe return (FuncStats.MaxSample >= HotThreshold); 1100*540489deSweihe } 1101*540489deSweihe 1102*540489deSweihe namespace { 1103*540489deSweihe class SampleOverlapAggregator { 1104*540489deSweihe public: 1105*540489deSweihe SampleOverlapAggregator(const std::string &BaseFilename, 1106*540489deSweihe const std::string &TestFilename, 1107*540489deSweihe double LowSimilarityThreshold, double Epsilon, 1108*540489deSweihe const OverlapFuncFilters &FuncFilter) 1109*540489deSweihe : BaseFilename(BaseFilename), TestFilename(TestFilename), 1110*540489deSweihe LowSimilarityThreshold(LowSimilarityThreshold), Epsilon(Epsilon), 1111*540489deSweihe FuncFilter(FuncFilter) {} 1112*540489deSweihe 1113*540489deSweihe /// Detect 0-sample input profile and report to output stream. This interface 1114*540489deSweihe /// should be called after loadProfiles(). 1115*540489deSweihe bool detectZeroSampleProfile(raw_fd_ostream &OS) const; 1116*540489deSweihe 1117*540489deSweihe /// Write out function-level similarity statistics for functions specified by 1118*540489deSweihe /// options --function, --value-cutoff, and --similarity-cutoff. 1119*540489deSweihe void dumpFuncSimilarity(raw_fd_ostream &OS) const; 1120*540489deSweihe 1121*540489deSweihe /// Write out program-level similarity and overlap statistics. 1122*540489deSweihe void dumpProgramSummary(raw_fd_ostream &OS) const; 1123*540489deSweihe 1124*540489deSweihe /// Write out hot-function and hot-block statistics for base_profile, 1125*540489deSweihe /// test_profile, and their overlap. For both cases, the overlap HO is 1126*540489deSweihe /// calculated as follows: 1127*540489deSweihe /// Given the number of functions (or blocks) that are hot in both profiles 1128*540489deSweihe /// HCommon and the number of functions (or blocks) that are hot in at 1129*540489deSweihe /// least one profile HUnion, HO = HCommon / HUnion. 1130*540489deSweihe void dumpHotFuncAndBlockOverlap(raw_fd_ostream &OS) const; 1131*540489deSweihe 1132*540489deSweihe /// This function tries matching functions in base and test profiles. For each 1133*540489deSweihe /// pair of matched functions, it aggregates the function-level 1134*540489deSweihe /// similarity into a profile-level similarity. It also dump function-level 1135*540489deSweihe /// similarity information of functions specified by --function, 1136*540489deSweihe /// --value-cutoff, and --similarity-cutoff options. The program-level 1137*540489deSweihe /// similarity PS is computed as follows: 1138*540489deSweihe /// Given function-level similarity FS(A) for all function A, the 1139*540489deSweihe /// weight of function A in base profile WB(A), and the weight of function 1140*540489deSweihe /// A in test profile WT(A), compute PS(base_profile, test_profile) = 1141*540489deSweihe /// sum_A(FS(A) * avg(WB(A), WT(A))) ranging in [0.0f to 1.0f] with 0.0 1142*540489deSweihe /// meaning no-overlap. 1143*540489deSweihe void computeSampleProfileOverlap(raw_fd_ostream &OS); 1144*540489deSweihe 1145*540489deSweihe /// Initialize ProfOverlap with the sum of samples in base and test 1146*540489deSweihe /// profiles. This function also computes and keeps the sum of samples and 1147*540489deSweihe /// max sample counts of each function in BaseStats and TestStats for later 1148*540489deSweihe /// use to avoid re-computations. 1149*540489deSweihe void initializeSampleProfileOverlap(); 1150*540489deSweihe 1151*540489deSweihe /// Load profiles specified by BaseFilename and TestFilename. 1152*540489deSweihe std::error_code loadProfiles(); 1153*540489deSweihe 1154*540489deSweihe private: 1155*540489deSweihe SampleOverlapStats ProfOverlap; 1156*540489deSweihe SampleOverlapStats HotFuncOverlap; 1157*540489deSweihe SampleOverlapStats HotBlockOverlap; 1158*540489deSweihe std::string BaseFilename; 1159*540489deSweihe std::string TestFilename; 1160*540489deSweihe std::unique_ptr<sampleprof::SampleProfileReader> BaseReader; 1161*540489deSweihe std::unique_ptr<sampleprof::SampleProfileReader> TestReader; 1162*540489deSweihe // BaseStats and TestStats hold FuncSampleStats for each function, with 1163*540489deSweihe // function name as the key. 1164*540489deSweihe StringMap<FuncSampleStats> BaseStats; 1165*540489deSweihe StringMap<FuncSampleStats> TestStats; 1166*540489deSweihe // Low similarity threshold in floating point number 1167*540489deSweihe double LowSimilarityThreshold; 1168*540489deSweihe // Block samples above BaseHotThreshold or TestHotThreshold are considered hot 1169*540489deSweihe // for tracking hot blocks. 1170*540489deSweihe uint64_t BaseHotThreshold; 1171*540489deSweihe uint64_t TestHotThreshold; 1172*540489deSweihe // A small threshold used to round the results of floating point accumulations 1173*540489deSweihe // to resolve imprecision. 1174*540489deSweihe const double Epsilon; 1175*540489deSweihe std::multimap<double, SampleOverlapStats, std::greater<double>> 1176*540489deSweihe FuncSimilarityDump; 1177*540489deSweihe // FuncFilter carries specifications in options --value-cutoff and 1178*540489deSweihe // --function. 1179*540489deSweihe OverlapFuncFilters FuncFilter; 1180*540489deSweihe // Column offsets for printing the function-level details table. 1181*540489deSweihe static const unsigned int TestWeightCol = 15; 1182*540489deSweihe static const unsigned int SimilarityCol = 30; 1183*540489deSweihe static const unsigned int OverlapCol = 43; 1184*540489deSweihe static const unsigned int BaseUniqueCol = 53; 1185*540489deSweihe static const unsigned int TestUniqueCol = 67; 1186*540489deSweihe static const unsigned int BaseSampleCol = 81; 1187*540489deSweihe static const unsigned int TestSampleCol = 96; 1188*540489deSweihe static const unsigned int FuncNameCol = 111; 1189*540489deSweihe 1190*540489deSweihe /// Return a similarity of two line/block sample counters in the same 1191*540489deSweihe /// function in base and test profiles. The line/block-similarity BS(i) is 1192*540489deSweihe /// computed as follows: 1193*540489deSweihe /// For an offsets i, given the sample count at i in base profile BB(i), 1194*540489deSweihe /// the sample count at i in test profile BT(i), the sum of sample counts 1195*540489deSweihe /// in this function in base profile SB, and the sum of sample counts in 1196*540489deSweihe /// this function in test profile ST, compute BS(i) = 1.0 - fabs(BB(i)/SB - 1197*540489deSweihe /// BT(i)/ST), ranging in [0.0f to 1.0f] with 0.0 meaning no-overlap. 1198*540489deSweihe double computeBlockSimilarity(uint64_t BaseSample, uint64_t TestSample, 1199*540489deSweihe const SampleOverlapStats &FuncOverlap) const; 1200*540489deSweihe 1201*540489deSweihe void updateHotBlockOverlap(uint64_t BaseSample, uint64_t TestSample, 1202*540489deSweihe uint64_t HotBlockCount); 1203*540489deSweihe 1204*540489deSweihe void getHotFunctions(const StringMap<FuncSampleStats> &ProfStats, 1205*540489deSweihe StringMap<FuncSampleStats> &HotFunc, 1206*540489deSweihe uint64_t HotThreshold) const; 1207*540489deSweihe 1208*540489deSweihe void computeHotFuncOverlap(); 1209*540489deSweihe 1210*540489deSweihe /// This function updates statistics in FuncOverlap, HotBlockOverlap, and 1211*540489deSweihe /// Difference for two sample units in a matched function according to the 1212*540489deSweihe /// given match status. 1213*540489deSweihe void updateOverlapStatsForFunction(uint64_t BaseSample, uint64_t TestSample, 1214*540489deSweihe uint64_t HotBlockCount, 1215*540489deSweihe SampleOverlapStats &FuncOverlap, 1216*540489deSweihe double &Difference, MatchStatus Status); 1217*540489deSweihe 1218*540489deSweihe /// This function updates statistics in FuncOverlap, HotBlockOverlap, and 1219*540489deSweihe /// Difference for unmatched callees that only present in one profile in a 1220*540489deSweihe /// matched caller function. 1221*540489deSweihe void updateForUnmatchedCallee(const sampleprof::FunctionSamples &Func, 1222*540489deSweihe SampleOverlapStats &FuncOverlap, 1223*540489deSweihe double &Difference, MatchStatus Status); 1224*540489deSweihe 1225*540489deSweihe /// This function updates sample overlap statistics of an overlap function in 1226*540489deSweihe /// base and test profile. It also calculates a function-internal similarity 1227*540489deSweihe /// FIS as follows: 1228*540489deSweihe /// For offsets i that have samples in at least one profile in this 1229*540489deSweihe /// function A, given BS(i) returned by computeBlockSimilarity(), compute 1230*540489deSweihe /// FIS(A) = (2.0 - sum_i(1.0 - BS(i))) / 2, ranging in [0.0f to 1.0f] with 1231*540489deSweihe /// 0.0 meaning no overlap. 1232*540489deSweihe double computeSampleFunctionInternalOverlap( 1233*540489deSweihe const sampleprof::FunctionSamples &BaseFunc, 1234*540489deSweihe const sampleprof::FunctionSamples &TestFunc, 1235*540489deSweihe SampleOverlapStats &FuncOverlap); 1236*540489deSweihe 1237*540489deSweihe /// Function-level similarity (FS) is a weighted value over function internal 1238*540489deSweihe /// similarity (FIS). This function computes a function's FS from its FIS by 1239*540489deSweihe /// applying the weight. 1240*540489deSweihe double weightForFuncSimilarity(double FuncSimilarity, uint64_t BaseFuncSample, 1241*540489deSweihe uint64_t TestFuncSample) const; 1242*540489deSweihe 1243*540489deSweihe /// The function-level similarity FS(A) for a function A is computed as 1244*540489deSweihe /// follows: 1245*540489deSweihe /// Compute a function-internal similarity FIS(A) by 1246*540489deSweihe /// computeSampleFunctionInternalOverlap(). Then, with the weight of 1247*540489deSweihe /// function A in base profile WB(A), and the weight of function A in test 1248*540489deSweihe /// profile WT(A), compute FS(A) = FIS(A) * (1.0 - fabs(WB(A) - WT(A))) 1249*540489deSweihe /// ranging in [0.0f to 1.0f] with 0.0 meaning no overlap. 1250*540489deSweihe double 1251*540489deSweihe computeSampleFunctionOverlap(const sampleprof::FunctionSamples *BaseFunc, 1252*540489deSweihe const sampleprof::FunctionSamples *TestFunc, 1253*540489deSweihe SampleOverlapStats *FuncOverlap, 1254*540489deSweihe uint64_t BaseFuncSample, 1255*540489deSweihe uint64_t TestFuncSample); 1256*540489deSweihe 1257*540489deSweihe /// Profile-level similarity (PS) is a weighted aggregate over function-level 1258*540489deSweihe /// similarities (FS). This method weights the FS value by the function 1259*540489deSweihe /// weights in the base and test profiles for the aggregation. 1260*540489deSweihe double weightByImportance(double FuncSimilarity, uint64_t BaseFuncSample, 1261*540489deSweihe uint64_t TestFuncSample) const; 1262*540489deSweihe }; 1263*540489deSweihe } // end anonymous namespace 1264*540489deSweihe 1265*540489deSweihe bool SampleOverlapAggregator::detectZeroSampleProfile( 1266*540489deSweihe raw_fd_ostream &OS) const { 1267*540489deSweihe bool HaveZeroSample = false; 1268*540489deSweihe if (ProfOverlap.BaseSample == 0) { 1269*540489deSweihe OS << "Sum of sample counts for profile " << BaseFilename << " is 0.\n"; 1270*540489deSweihe HaveZeroSample = true; 1271*540489deSweihe } 1272*540489deSweihe if (ProfOverlap.TestSample == 0) { 1273*540489deSweihe OS << "Sum of sample counts for profile " << TestFilename << " is 0.\n"; 1274*540489deSweihe HaveZeroSample = true; 1275*540489deSweihe } 1276*540489deSweihe return HaveZeroSample; 1277*540489deSweihe } 1278*540489deSweihe 1279*540489deSweihe double SampleOverlapAggregator::computeBlockSimilarity( 1280*540489deSweihe uint64_t BaseSample, uint64_t TestSample, 1281*540489deSweihe const SampleOverlapStats &FuncOverlap) const { 1282*540489deSweihe double BaseFrac = 0.0; 1283*540489deSweihe double TestFrac = 0.0; 1284*540489deSweihe if (FuncOverlap.BaseSample > 0) 1285*540489deSweihe BaseFrac = static_cast<double>(BaseSample) / FuncOverlap.BaseSample; 1286*540489deSweihe if (FuncOverlap.TestSample > 0) 1287*540489deSweihe TestFrac = static_cast<double>(TestSample) / FuncOverlap.TestSample; 1288*540489deSweihe return 1.0 - std::fabs(BaseFrac - TestFrac); 1289*540489deSweihe } 1290*540489deSweihe 1291*540489deSweihe void SampleOverlapAggregator::updateHotBlockOverlap(uint64_t BaseSample, 1292*540489deSweihe uint64_t TestSample, 1293*540489deSweihe uint64_t HotBlockCount) { 1294*540489deSweihe bool IsBaseHot = (BaseSample >= BaseHotThreshold); 1295*540489deSweihe bool IsTestHot = (TestSample >= TestHotThreshold); 1296*540489deSweihe if (!IsBaseHot && !IsTestHot) 1297*540489deSweihe return; 1298*540489deSweihe 1299*540489deSweihe HotBlockOverlap.UnionCount += HotBlockCount; 1300*540489deSweihe if (IsBaseHot) 1301*540489deSweihe HotBlockOverlap.BaseCount += HotBlockCount; 1302*540489deSweihe if (IsTestHot) 1303*540489deSweihe HotBlockOverlap.TestCount += HotBlockCount; 1304*540489deSweihe if (IsBaseHot && IsTestHot) 1305*540489deSweihe HotBlockOverlap.OverlapCount += HotBlockCount; 1306*540489deSweihe } 1307*540489deSweihe 1308*540489deSweihe void SampleOverlapAggregator::getHotFunctions( 1309*540489deSweihe const StringMap<FuncSampleStats> &ProfStats, 1310*540489deSweihe StringMap<FuncSampleStats> &HotFunc, uint64_t HotThreshold) const { 1311*540489deSweihe for (const auto &F : ProfStats) { 1312*540489deSweihe if (isFunctionHot(F.second, HotThreshold)) 1313*540489deSweihe HotFunc.try_emplace(F.first(), F.second); 1314*540489deSweihe } 1315*540489deSweihe } 1316*540489deSweihe 1317*540489deSweihe void SampleOverlapAggregator::computeHotFuncOverlap() { 1318*540489deSweihe StringMap<FuncSampleStats> BaseHotFunc; 1319*540489deSweihe getHotFunctions(BaseStats, BaseHotFunc, BaseHotThreshold); 1320*540489deSweihe HotFuncOverlap.BaseCount = BaseHotFunc.size(); 1321*540489deSweihe 1322*540489deSweihe StringMap<FuncSampleStats> TestHotFunc; 1323*540489deSweihe getHotFunctions(TestStats, TestHotFunc, TestHotThreshold); 1324*540489deSweihe HotFuncOverlap.TestCount = TestHotFunc.size(); 1325*540489deSweihe HotFuncOverlap.UnionCount = HotFuncOverlap.TestCount; 1326*540489deSweihe 1327*540489deSweihe for (const auto &F : BaseHotFunc) { 1328*540489deSweihe if (TestHotFunc.count(F.first())) 1329*540489deSweihe ++HotFuncOverlap.OverlapCount; 1330*540489deSweihe else 1331*540489deSweihe ++HotFuncOverlap.UnionCount; 1332*540489deSweihe } 1333*540489deSweihe } 1334*540489deSweihe 1335*540489deSweihe void SampleOverlapAggregator::updateOverlapStatsForFunction( 1336*540489deSweihe uint64_t BaseSample, uint64_t TestSample, uint64_t HotBlockCount, 1337*540489deSweihe SampleOverlapStats &FuncOverlap, double &Difference, MatchStatus Status) { 1338*540489deSweihe assert(Status != MS_None && 1339*540489deSweihe "Match status should be updated before updating overlap statistics"); 1340*540489deSweihe if (Status == MS_FirstUnique) { 1341*540489deSweihe TestSample = 0; 1342*540489deSweihe FuncOverlap.BaseUniqueSample += BaseSample; 1343*540489deSweihe } else if (Status == MS_SecondUnique) { 1344*540489deSweihe BaseSample = 0; 1345*540489deSweihe FuncOverlap.TestUniqueSample += TestSample; 1346*540489deSweihe } else { 1347*540489deSweihe ++FuncOverlap.OverlapCount; 1348*540489deSweihe } 1349*540489deSweihe 1350*540489deSweihe FuncOverlap.UnionSample += std::max(BaseSample, TestSample); 1351*540489deSweihe FuncOverlap.OverlapSample += std::min(BaseSample, TestSample); 1352*540489deSweihe Difference += 1353*540489deSweihe 1.0 - computeBlockSimilarity(BaseSample, TestSample, FuncOverlap); 1354*540489deSweihe updateHotBlockOverlap(BaseSample, TestSample, HotBlockCount); 1355*540489deSweihe } 1356*540489deSweihe 1357*540489deSweihe void SampleOverlapAggregator::updateForUnmatchedCallee( 1358*540489deSweihe const sampleprof::FunctionSamples &Func, SampleOverlapStats &FuncOverlap, 1359*540489deSweihe double &Difference, MatchStatus Status) { 1360*540489deSweihe assert((Status == MS_FirstUnique || Status == MS_SecondUnique) && 1361*540489deSweihe "Status must be either of the two unmatched cases"); 1362*540489deSweihe FuncSampleStats FuncStats; 1363*540489deSweihe if (Status == MS_FirstUnique) { 1364*540489deSweihe getFuncSampleStats(Func, FuncStats, BaseHotThreshold); 1365*540489deSweihe updateOverlapStatsForFunction(FuncStats.SampleSum, 0, 1366*540489deSweihe FuncStats.HotBlockCount, FuncOverlap, 1367*540489deSweihe Difference, Status); 1368*540489deSweihe } else { 1369*540489deSweihe getFuncSampleStats(Func, FuncStats, TestHotThreshold); 1370*540489deSweihe updateOverlapStatsForFunction(0, FuncStats.SampleSum, 1371*540489deSweihe FuncStats.HotBlockCount, FuncOverlap, 1372*540489deSweihe Difference, Status); 1373*540489deSweihe } 1374*540489deSweihe } 1375*540489deSweihe 1376*540489deSweihe double SampleOverlapAggregator::computeSampleFunctionInternalOverlap( 1377*540489deSweihe const sampleprof::FunctionSamples &BaseFunc, 1378*540489deSweihe const sampleprof::FunctionSamples &TestFunc, 1379*540489deSweihe SampleOverlapStats &FuncOverlap) { 1380*540489deSweihe 1381*540489deSweihe using namespace sampleprof; 1382*540489deSweihe 1383*540489deSweihe double Difference = 0; 1384*540489deSweihe 1385*540489deSweihe // Accumulate Difference for regular line/block samples in the function. 1386*540489deSweihe // We match them through sort-merge join algorithm because 1387*540489deSweihe // FunctionSamples::getBodySamples() returns a map of sample counters ordered 1388*540489deSweihe // by their offsets. 1389*540489deSweihe MatchStep<BodySampleMap::const_iterator> BlockIterStep( 1390*540489deSweihe BaseFunc.getBodySamples().cbegin(), BaseFunc.getBodySamples().cend(), 1391*540489deSweihe TestFunc.getBodySamples().cbegin(), TestFunc.getBodySamples().cend()); 1392*540489deSweihe BlockIterStep.updateOneStep(); 1393*540489deSweihe while (!BlockIterStep.areBothFinished()) { 1394*540489deSweihe uint64_t BaseSample = 1395*540489deSweihe BlockIterStep.isFirstFinished() 1396*540489deSweihe ? 0 1397*540489deSweihe : BlockIterStep.getFirstIter()->second.getSamples(); 1398*540489deSweihe uint64_t TestSample = 1399*540489deSweihe BlockIterStep.isSecondFinished() 1400*540489deSweihe ? 0 1401*540489deSweihe : BlockIterStep.getSecondIter()->second.getSamples(); 1402*540489deSweihe updateOverlapStatsForFunction(BaseSample, TestSample, 1, FuncOverlap, 1403*540489deSweihe Difference, BlockIterStep.getMatchStatus()); 1404*540489deSweihe 1405*540489deSweihe BlockIterStep.updateOneStep(); 1406*540489deSweihe } 1407*540489deSweihe 1408*540489deSweihe // Accumulate Difference for callsite lines in the function. We match 1409*540489deSweihe // them through sort-merge algorithm because 1410*540489deSweihe // FunctionSamples::getCallsiteSamples() returns a map of callsite records 1411*540489deSweihe // ordered by their offsets. 1412*540489deSweihe MatchStep<CallsiteSampleMap::const_iterator> CallsiteIterStep( 1413*540489deSweihe BaseFunc.getCallsiteSamples().cbegin(), 1414*540489deSweihe BaseFunc.getCallsiteSamples().cend(), 1415*540489deSweihe TestFunc.getCallsiteSamples().cbegin(), 1416*540489deSweihe TestFunc.getCallsiteSamples().cend()); 1417*540489deSweihe CallsiteIterStep.updateOneStep(); 1418*540489deSweihe while (!CallsiteIterStep.areBothFinished()) { 1419*540489deSweihe MatchStatus CallsiteStepStatus = CallsiteIterStep.getMatchStatus(); 1420*540489deSweihe assert(CallsiteStepStatus != MS_None && 1421*540489deSweihe "Match status should be updated before entering loop body"); 1422*540489deSweihe 1423*540489deSweihe if (CallsiteStepStatus != MS_Match) { 1424*540489deSweihe auto Callsite = (CallsiteStepStatus == MS_FirstUnique) 1425*540489deSweihe ? CallsiteIterStep.getFirstIter() 1426*540489deSweihe : CallsiteIterStep.getSecondIter(); 1427*540489deSweihe for (const auto &F : Callsite->second) 1428*540489deSweihe updateForUnmatchedCallee(F.second, FuncOverlap, Difference, 1429*540489deSweihe CallsiteStepStatus); 1430*540489deSweihe } else { 1431*540489deSweihe // There may be multiple inlinees at the same offset, so we need to try 1432*540489deSweihe // matching all of them. This match is implemented through sort-merge 1433*540489deSweihe // algorithm because callsite records at the same offset are ordered by 1434*540489deSweihe // function names. 1435*540489deSweihe MatchStep<FunctionSamplesMap::const_iterator> CalleeIterStep( 1436*540489deSweihe CallsiteIterStep.getFirstIter()->second.cbegin(), 1437*540489deSweihe CallsiteIterStep.getFirstIter()->second.cend(), 1438*540489deSweihe CallsiteIterStep.getSecondIter()->second.cbegin(), 1439*540489deSweihe CallsiteIterStep.getSecondIter()->second.cend()); 1440*540489deSweihe CalleeIterStep.updateOneStep(); 1441*540489deSweihe while (!CalleeIterStep.areBothFinished()) { 1442*540489deSweihe MatchStatus CalleeStepStatus = CalleeIterStep.getMatchStatus(); 1443*540489deSweihe if (CalleeStepStatus != MS_Match) { 1444*540489deSweihe auto Callee = (CalleeStepStatus == MS_FirstUnique) 1445*540489deSweihe ? CalleeIterStep.getFirstIter() 1446*540489deSweihe : CalleeIterStep.getSecondIter(); 1447*540489deSweihe updateForUnmatchedCallee(Callee->second, FuncOverlap, Difference, 1448*540489deSweihe CalleeStepStatus); 1449*540489deSweihe } else { 1450*540489deSweihe // An inlined function can contain other inlinees inside, so compute 1451*540489deSweihe // the Difference recursively. 1452*540489deSweihe Difference += 2.0 - 2 * computeSampleFunctionInternalOverlap( 1453*540489deSweihe CalleeIterStep.getFirstIter()->second, 1454*540489deSweihe CalleeIterStep.getSecondIter()->second, 1455*540489deSweihe FuncOverlap); 1456*540489deSweihe } 1457*540489deSweihe CalleeIterStep.updateOneStep(); 1458*540489deSweihe } 1459*540489deSweihe } 1460*540489deSweihe CallsiteIterStep.updateOneStep(); 1461*540489deSweihe } 1462*540489deSweihe 1463*540489deSweihe // Difference reflects the total differences of line/block samples in this 1464*540489deSweihe // function and ranges in [0.0f to 2.0f]. Take (2.0 - Difference) / 2 to 1465*540489deSweihe // reflect the similarity between function profiles in [0.0f to 1.0f]. 1466*540489deSweihe return (2.0 - Difference) / 2; 1467*540489deSweihe } 1468*540489deSweihe 1469*540489deSweihe double SampleOverlapAggregator::weightForFuncSimilarity( 1470*540489deSweihe double FuncInternalSimilarity, uint64_t BaseFuncSample, 1471*540489deSweihe uint64_t TestFuncSample) const { 1472*540489deSweihe // Compute the weight as the distance between the function weights in two 1473*540489deSweihe // profiles. 1474*540489deSweihe double BaseFrac = 0.0; 1475*540489deSweihe double TestFrac = 0.0; 1476*540489deSweihe assert(ProfOverlap.BaseSample > 0 && 1477*540489deSweihe "Total samples in base profile should be greater than 0"); 1478*540489deSweihe BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample; 1479*540489deSweihe assert(ProfOverlap.TestSample > 0 && 1480*540489deSweihe "Total samples in test profile should be greater than 0"); 1481*540489deSweihe TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample; 1482*540489deSweihe double WeightDistance = std::fabs(BaseFrac - TestFrac); 1483*540489deSweihe 1484*540489deSweihe // Take WeightDistance into the similarity. 1485*540489deSweihe return FuncInternalSimilarity * (1 - WeightDistance); 1486*540489deSweihe } 1487*540489deSweihe 1488*540489deSweihe double 1489*540489deSweihe SampleOverlapAggregator::weightByImportance(double FuncSimilarity, 1490*540489deSweihe uint64_t BaseFuncSample, 1491*540489deSweihe uint64_t TestFuncSample) const { 1492*540489deSweihe 1493*540489deSweihe double BaseFrac = 0.0; 1494*540489deSweihe double TestFrac = 0.0; 1495*540489deSweihe assert(ProfOverlap.BaseSample > 0 && 1496*540489deSweihe "Total samples in base profile should be greater than 0"); 1497*540489deSweihe BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample / 2.0; 1498*540489deSweihe assert(ProfOverlap.TestSample > 0 && 1499*540489deSweihe "Total samples in test profile should be greater than 0"); 1500*540489deSweihe TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample / 2.0; 1501*540489deSweihe return FuncSimilarity * (BaseFrac + TestFrac); 1502*540489deSweihe } 1503*540489deSweihe 1504*540489deSweihe double SampleOverlapAggregator::computeSampleFunctionOverlap( 1505*540489deSweihe const sampleprof::FunctionSamples *BaseFunc, 1506*540489deSweihe const sampleprof::FunctionSamples *TestFunc, 1507*540489deSweihe SampleOverlapStats *FuncOverlap, uint64_t BaseFuncSample, 1508*540489deSweihe uint64_t TestFuncSample) { 1509*540489deSweihe // Default function internal similarity before weighted, meaning two functions 1510*540489deSweihe // has no overlap. 1511*540489deSweihe const double DefaultFuncInternalSimilarity = 0; 1512*540489deSweihe double FuncSimilarity; 1513*540489deSweihe double FuncInternalSimilarity; 1514*540489deSweihe 1515*540489deSweihe // If BaseFunc or TestFunc is nullptr, it means the functions do not overlap. 1516*540489deSweihe // In this case, we use DefaultFuncInternalSimilarity as the function internal 1517*540489deSweihe // similarity. 1518*540489deSweihe if (!BaseFunc || !TestFunc) { 1519*540489deSweihe FuncInternalSimilarity = DefaultFuncInternalSimilarity; 1520*540489deSweihe } else { 1521*540489deSweihe assert(FuncOverlap != nullptr && 1522*540489deSweihe "FuncOverlap should be provided in this case"); 1523*540489deSweihe FuncInternalSimilarity = computeSampleFunctionInternalOverlap( 1524*540489deSweihe *BaseFunc, *TestFunc, *FuncOverlap); 1525*540489deSweihe // Now, FuncInternalSimilarity may be a little less than 0 due to 1526*540489deSweihe // imprecision of floating point accumulations. Make it zero if the 1527*540489deSweihe // difference is below Epsilon. 1528*540489deSweihe FuncInternalSimilarity = (std::fabs(FuncInternalSimilarity - 0) < Epsilon) 1529*540489deSweihe ? 0 1530*540489deSweihe : FuncInternalSimilarity; 1531*540489deSweihe } 1532*540489deSweihe FuncSimilarity = weightForFuncSimilarity(FuncInternalSimilarity, 1533*540489deSweihe BaseFuncSample, TestFuncSample); 1534*540489deSweihe return FuncSimilarity; 1535*540489deSweihe } 1536*540489deSweihe 1537*540489deSweihe void SampleOverlapAggregator::computeSampleProfileOverlap(raw_fd_ostream &OS) { 1538*540489deSweihe using namespace sampleprof; 1539*540489deSweihe 1540*540489deSweihe StringMap<const FunctionSamples *> BaseFuncProf; 1541*540489deSweihe const auto &BaseProfiles = BaseReader->getProfiles(); 1542*540489deSweihe for (const auto &BaseFunc : BaseProfiles) { 1543*540489deSweihe BaseFuncProf.try_emplace(BaseFunc.second.getFuncName(), &(BaseFunc.second)); 1544*540489deSweihe } 1545*540489deSweihe ProfOverlap.UnionCount = BaseFuncProf.size(); 1546*540489deSweihe 1547*540489deSweihe const auto &TestProfiles = TestReader->getProfiles(); 1548*540489deSweihe for (const auto &TestFunc : TestProfiles) { 1549*540489deSweihe SampleOverlapStats FuncOverlap; 1550*540489deSweihe FuncOverlap.TestName = TestFunc.second.getFuncName(); 1551*540489deSweihe assert(TestStats.count(FuncOverlap.TestName) && 1552*540489deSweihe "TestStats should have records for all functions in test profile " 1553*540489deSweihe "except inlinees"); 1554*540489deSweihe FuncOverlap.TestSample = TestStats[FuncOverlap.TestName].SampleSum; 1555*540489deSweihe 1556*540489deSweihe const auto Match = BaseFuncProf.find(FuncOverlap.TestName); 1557*540489deSweihe if (Match == BaseFuncProf.end()) { 1558*540489deSweihe const FuncSampleStats &FuncStats = TestStats[FuncOverlap.TestName]; 1559*540489deSweihe ++ProfOverlap.TestUniqueCount; 1560*540489deSweihe ProfOverlap.TestUniqueSample += FuncStats.SampleSum; 1561*540489deSweihe FuncOverlap.TestUniqueSample = FuncStats.SampleSum; 1562*540489deSweihe 1563*540489deSweihe updateHotBlockOverlap(0, FuncStats.SampleSum, FuncStats.HotBlockCount); 1564*540489deSweihe 1565*540489deSweihe double FuncSimilarity = computeSampleFunctionOverlap( 1566*540489deSweihe nullptr, nullptr, nullptr, 0, FuncStats.SampleSum); 1567*540489deSweihe ProfOverlap.Similarity += 1568*540489deSweihe weightByImportance(FuncSimilarity, 0, FuncStats.SampleSum); 1569*540489deSweihe 1570*540489deSweihe ++ProfOverlap.UnionCount; 1571*540489deSweihe ProfOverlap.UnionSample += FuncStats.SampleSum; 1572*540489deSweihe } else { 1573*540489deSweihe ++ProfOverlap.OverlapCount; 1574*540489deSweihe 1575*540489deSweihe // Two functions match with each other. Compute function-level overlap and 1576*540489deSweihe // aggregate them into profile-level overlap. 1577*540489deSweihe FuncOverlap.BaseName = Match->second->getFuncName(); 1578*540489deSweihe assert(BaseStats.count(FuncOverlap.BaseName) && 1579*540489deSweihe "BaseStats should have records for all functions in base profile " 1580*540489deSweihe "except inlinees"); 1581*540489deSweihe FuncOverlap.BaseSample = BaseStats[FuncOverlap.BaseName].SampleSum; 1582*540489deSweihe 1583*540489deSweihe FuncOverlap.Similarity = computeSampleFunctionOverlap( 1584*540489deSweihe Match->second, &TestFunc.second, &FuncOverlap, FuncOverlap.BaseSample, 1585*540489deSweihe FuncOverlap.TestSample); 1586*540489deSweihe ProfOverlap.Similarity += 1587*540489deSweihe weightByImportance(FuncOverlap.Similarity, FuncOverlap.BaseSample, 1588*540489deSweihe FuncOverlap.TestSample); 1589*540489deSweihe ProfOverlap.OverlapSample += FuncOverlap.OverlapSample; 1590*540489deSweihe ProfOverlap.UnionSample += FuncOverlap.UnionSample; 1591*540489deSweihe 1592*540489deSweihe // Accumulate the percentage of base unique and test unique samples into 1593*540489deSweihe // ProfOverlap. 1594*540489deSweihe ProfOverlap.BaseUniqueSample += FuncOverlap.BaseUniqueSample; 1595*540489deSweihe ProfOverlap.TestUniqueSample += FuncOverlap.TestUniqueSample; 1596*540489deSweihe 1597*540489deSweihe // Remove matched base functions for later reporting functions not found 1598*540489deSweihe // in test profile. 1599*540489deSweihe BaseFuncProf.erase(Match); 1600*540489deSweihe } 1601*540489deSweihe 1602*540489deSweihe // Print function-level similarity information if specified by options. 1603*540489deSweihe assert(TestStats.count(FuncOverlap.TestName) && 1604*540489deSweihe "TestStats should have records for all functions in test profile " 1605*540489deSweihe "except inlinees"); 1606*540489deSweihe if (TestStats[FuncOverlap.TestName].MaxSample >= FuncFilter.ValueCutoff || 1607*540489deSweihe (Match != BaseFuncProf.end() && 1608*540489deSweihe FuncOverlap.Similarity < LowSimilarityThreshold) || 1609*540489deSweihe (Match != BaseFuncProf.end() && !FuncFilter.NameFilter.empty() && 1610*540489deSweihe FuncOverlap.BaseName.find(FuncFilter.NameFilter) != 1611*540489deSweihe FuncOverlap.BaseName.npos)) { 1612*540489deSweihe assert(ProfOverlap.BaseSample > 0 && 1613*540489deSweihe "Total samples in base profile should be greater than 0"); 1614*540489deSweihe FuncOverlap.BaseWeight = 1615*540489deSweihe static_cast<double>(FuncOverlap.BaseSample) / ProfOverlap.BaseSample; 1616*540489deSweihe assert(ProfOverlap.TestSample > 0 && 1617*540489deSweihe "Total samples in test profile should be greater than 0"); 1618*540489deSweihe FuncOverlap.TestWeight = 1619*540489deSweihe static_cast<double>(FuncOverlap.TestSample) / ProfOverlap.TestSample; 1620*540489deSweihe FuncSimilarityDump.emplace(FuncOverlap.BaseWeight, FuncOverlap); 1621*540489deSweihe } 1622*540489deSweihe } 1623*540489deSweihe 1624*540489deSweihe // Traverse through functions in base profile but not in test profile. 1625*540489deSweihe for (const auto &F : BaseFuncProf) { 1626*540489deSweihe assert(BaseStats.count(F.second->getFuncName()) && 1627*540489deSweihe "BaseStats should have records for all functions in base profile " 1628*540489deSweihe "except inlinees"); 1629*540489deSweihe const FuncSampleStats &FuncStats = BaseStats[F.second->getFuncName()]; 1630*540489deSweihe ++ProfOverlap.BaseUniqueCount; 1631*540489deSweihe ProfOverlap.BaseUniqueSample += FuncStats.SampleSum; 1632*540489deSweihe 1633*540489deSweihe updateHotBlockOverlap(FuncStats.SampleSum, 0, FuncStats.HotBlockCount); 1634*540489deSweihe 1635*540489deSweihe double FuncSimilarity = computeSampleFunctionOverlap( 1636*540489deSweihe nullptr, nullptr, nullptr, FuncStats.SampleSum, 0); 1637*540489deSweihe ProfOverlap.Similarity += 1638*540489deSweihe weightByImportance(FuncSimilarity, FuncStats.SampleSum, 0); 1639*540489deSweihe 1640*540489deSweihe ProfOverlap.UnionSample += FuncStats.SampleSum; 1641*540489deSweihe } 1642*540489deSweihe 1643*540489deSweihe // Now, ProfSimilarity may be a little greater than 1 due to imprecision 1644*540489deSweihe // of floating point accumulations. Make it 1.0 if the difference is below 1645*540489deSweihe // Epsilon. 1646*540489deSweihe ProfOverlap.Similarity = (std::fabs(ProfOverlap.Similarity - 1) < Epsilon) 1647*540489deSweihe ? 1 1648*540489deSweihe : ProfOverlap.Similarity; 1649*540489deSweihe 1650*540489deSweihe computeHotFuncOverlap(); 1651*540489deSweihe } 1652*540489deSweihe 1653*540489deSweihe void SampleOverlapAggregator::initializeSampleProfileOverlap() { 1654*540489deSweihe const auto &BaseProf = BaseReader->getProfiles(); 1655*540489deSweihe for (const auto &I : BaseProf) { 1656*540489deSweihe ++ProfOverlap.BaseCount; 1657*540489deSweihe FuncSampleStats FuncStats; 1658*540489deSweihe getFuncSampleStats(I.second, FuncStats, BaseHotThreshold); 1659*540489deSweihe ProfOverlap.BaseSample += FuncStats.SampleSum; 1660*540489deSweihe BaseStats.try_emplace(I.second.getFuncName(), FuncStats); 1661*540489deSweihe } 1662*540489deSweihe 1663*540489deSweihe const auto &TestProf = TestReader->getProfiles(); 1664*540489deSweihe for (const auto &I : TestProf) { 1665*540489deSweihe ++ProfOverlap.TestCount; 1666*540489deSweihe FuncSampleStats FuncStats; 1667*540489deSweihe getFuncSampleStats(I.second, FuncStats, TestHotThreshold); 1668*540489deSweihe ProfOverlap.TestSample += FuncStats.SampleSum; 1669*540489deSweihe TestStats.try_emplace(I.second.getFuncName(), FuncStats); 1670*540489deSweihe } 1671*540489deSweihe 1672*540489deSweihe ProfOverlap.BaseName = StringRef(BaseFilename); 1673*540489deSweihe ProfOverlap.TestName = StringRef(TestFilename); 1674*540489deSweihe } 1675*540489deSweihe 1676*540489deSweihe void SampleOverlapAggregator::dumpFuncSimilarity(raw_fd_ostream &OS) const { 1677*540489deSweihe using namespace sampleprof; 1678*540489deSweihe 1679*540489deSweihe if (FuncSimilarityDump.empty()) 1680*540489deSweihe return; 1681*540489deSweihe 1682*540489deSweihe formatted_raw_ostream FOS(OS); 1683*540489deSweihe FOS << "Function-level details:\n"; 1684*540489deSweihe FOS << "Base weight"; 1685*540489deSweihe FOS.PadToColumn(TestWeightCol); 1686*540489deSweihe FOS << "Test weight"; 1687*540489deSweihe FOS.PadToColumn(SimilarityCol); 1688*540489deSweihe FOS << "Similarity"; 1689*540489deSweihe FOS.PadToColumn(OverlapCol); 1690*540489deSweihe FOS << "Overlap"; 1691*540489deSweihe FOS.PadToColumn(BaseUniqueCol); 1692*540489deSweihe FOS << "Base unique"; 1693*540489deSweihe FOS.PadToColumn(TestUniqueCol); 1694*540489deSweihe FOS << "Test unique"; 1695*540489deSweihe FOS.PadToColumn(BaseSampleCol); 1696*540489deSweihe FOS << "Base samples"; 1697*540489deSweihe FOS.PadToColumn(TestSampleCol); 1698*540489deSweihe FOS << "Test samples"; 1699*540489deSweihe FOS.PadToColumn(FuncNameCol); 1700*540489deSweihe FOS << "Function name\n"; 1701*540489deSweihe for (const auto &F : FuncSimilarityDump) { 1702*540489deSweihe double OverlapPercent = 1703*540489deSweihe F.second.UnionSample > 0 1704*540489deSweihe ? static_cast<double>(F.second.OverlapSample) / F.second.UnionSample 1705*540489deSweihe : 0; 1706*540489deSweihe double BaseUniquePercent = 1707*540489deSweihe F.second.BaseSample > 0 1708*540489deSweihe ? static_cast<double>(F.second.BaseUniqueSample) / 1709*540489deSweihe F.second.BaseSample 1710*540489deSweihe : 0; 1711*540489deSweihe double TestUniquePercent = 1712*540489deSweihe F.second.TestSample > 0 1713*540489deSweihe ? static_cast<double>(F.second.TestUniqueSample) / 1714*540489deSweihe F.second.TestSample 1715*540489deSweihe : 0; 1716*540489deSweihe 1717*540489deSweihe FOS << format("%.2f%%", F.second.BaseWeight * 100); 1718*540489deSweihe FOS.PadToColumn(TestWeightCol); 1719*540489deSweihe FOS << format("%.2f%%", F.second.TestWeight * 100); 1720*540489deSweihe FOS.PadToColumn(SimilarityCol); 1721*540489deSweihe FOS << format("%.2f%%", F.second.Similarity * 100); 1722*540489deSweihe FOS.PadToColumn(OverlapCol); 1723*540489deSweihe FOS << format("%.2f%%", OverlapPercent * 100); 1724*540489deSweihe FOS.PadToColumn(BaseUniqueCol); 1725*540489deSweihe FOS << format("%.2f%%", BaseUniquePercent * 100); 1726*540489deSweihe FOS.PadToColumn(TestUniqueCol); 1727*540489deSweihe FOS << format("%.2f%%", TestUniquePercent * 100); 1728*540489deSweihe FOS.PadToColumn(BaseSampleCol); 1729*540489deSweihe FOS << F.second.BaseSample; 1730*540489deSweihe FOS.PadToColumn(TestSampleCol); 1731*540489deSweihe FOS << F.second.TestSample; 1732*540489deSweihe FOS.PadToColumn(FuncNameCol); 1733*540489deSweihe FOS << F.second.TestName << "\n"; 1734*540489deSweihe } 1735*540489deSweihe } 1736*540489deSweihe 1737*540489deSweihe void SampleOverlapAggregator::dumpProgramSummary(raw_fd_ostream &OS) const { 1738*540489deSweihe OS << "Profile overlap infomation for base_profile: " << ProfOverlap.BaseName 1739*540489deSweihe << " and test_profile: " << ProfOverlap.TestName << "\nProgram level:\n"; 1740*540489deSweihe 1741*540489deSweihe OS << " Whole program profile similarity: " 1742*540489deSweihe << format("%.3f%%", ProfOverlap.Similarity * 100) << "\n"; 1743*540489deSweihe 1744*540489deSweihe assert(ProfOverlap.UnionSample > 0 && 1745*540489deSweihe "Total samples in two profile should be greater than 0"); 1746*540489deSweihe double OverlapPercent = 1747*540489deSweihe static_cast<double>(ProfOverlap.OverlapSample) / ProfOverlap.UnionSample; 1748*540489deSweihe assert(ProfOverlap.BaseSample > 0 && 1749*540489deSweihe "Total samples in base profile should be greater than 0"); 1750*540489deSweihe double BaseUniquePercent = static_cast<double>(ProfOverlap.BaseUniqueSample) / 1751*540489deSweihe ProfOverlap.BaseSample; 1752*540489deSweihe assert(ProfOverlap.TestSample > 0 && 1753*540489deSweihe "Total samples in test profile should be greater than 0"); 1754*540489deSweihe double TestUniquePercent = static_cast<double>(ProfOverlap.TestUniqueSample) / 1755*540489deSweihe ProfOverlap.TestSample; 1756*540489deSweihe 1757*540489deSweihe OS << " Whole program sample overlap: " 1758*540489deSweihe << format("%.3f%%", OverlapPercent * 100) << "\n"; 1759*540489deSweihe OS << " percentage of samples unique in base profile: " 1760*540489deSweihe << format("%.3f%%", BaseUniquePercent * 100) << "\n"; 1761*540489deSweihe OS << " percentage of samples unique in test profile: " 1762*540489deSweihe << format("%.3f%%", TestUniquePercent * 100) << "\n"; 1763*540489deSweihe OS << " total samples in base profile: " << ProfOverlap.BaseSample << "\n" 1764*540489deSweihe << " total samples in test profile: " << ProfOverlap.TestSample << "\n"; 1765*540489deSweihe 1766*540489deSweihe assert(ProfOverlap.UnionCount > 0 && 1767*540489deSweihe "There should be at least one function in two input profiles"); 1768*540489deSweihe double FuncOverlapPercent = 1769*540489deSweihe static_cast<double>(ProfOverlap.OverlapCount) / ProfOverlap.UnionCount; 1770*540489deSweihe OS << " Function overlap: " << format("%.3f%%", FuncOverlapPercent * 100) 1771*540489deSweihe << "\n"; 1772*540489deSweihe OS << " overlap functions: " << ProfOverlap.OverlapCount << "\n"; 1773*540489deSweihe OS << " functions unique in base profile: " << ProfOverlap.BaseUniqueCount 1774*540489deSweihe << "\n"; 1775*540489deSweihe OS << " functions unique in test profile: " << ProfOverlap.TestUniqueCount 1776*540489deSweihe << "\n"; 1777*540489deSweihe } 1778*540489deSweihe 1779*540489deSweihe void SampleOverlapAggregator::dumpHotFuncAndBlockOverlap( 1780*540489deSweihe raw_fd_ostream &OS) const { 1781*540489deSweihe assert(HotFuncOverlap.UnionCount > 0 && 1782*540489deSweihe "There should be at least one hot function in two input profiles"); 1783*540489deSweihe OS << " Hot-function overlap: " 1784*540489deSweihe << format("%.3f%%", static_cast<double>(HotFuncOverlap.OverlapCount) / 1785*540489deSweihe HotFuncOverlap.UnionCount * 100) 1786*540489deSweihe << "\n"; 1787*540489deSweihe OS << " overlap hot functions: " << HotFuncOverlap.OverlapCount << "\n"; 1788*540489deSweihe OS << " hot functions unique in base profile: " 1789*540489deSweihe << HotFuncOverlap.BaseCount - HotFuncOverlap.OverlapCount << "\n"; 1790*540489deSweihe OS << " hot functions unique in test profile: " 1791*540489deSweihe << HotFuncOverlap.TestCount - HotFuncOverlap.OverlapCount << "\n"; 1792*540489deSweihe 1793*540489deSweihe assert(HotBlockOverlap.UnionCount > 0 && 1794*540489deSweihe "There should be at least one hot block in two input profiles"); 1795*540489deSweihe OS << " Hot-block overlap: " 1796*540489deSweihe << format("%.3f%%", static_cast<double>(HotBlockOverlap.OverlapCount) / 1797*540489deSweihe HotBlockOverlap.UnionCount * 100) 1798*540489deSweihe << "\n"; 1799*540489deSweihe OS << " overlap hot blocks: " << HotBlockOverlap.OverlapCount << "\n"; 1800*540489deSweihe OS << " hot blocks unique in base profile: " 1801*540489deSweihe << HotBlockOverlap.BaseCount - HotBlockOverlap.OverlapCount << "\n"; 1802*540489deSweihe OS << " hot blocks unique in test profile: " 1803*540489deSweihe << HotBlockOverlap.TestCount - HotBlockOverlap.OverlapCount << "\n"; 1804*540489deSweihe } 1805*540489deSweihe 1806*540489deSweihe std::error_code SampleOverlapAggregator::loadProfiles() { 1807*540489deSweihe using namespace sampleprof; 1808*540489deSweihe 1809*540489deSweihe LLVMContext Context; 1810*540489deSweihe auto BaseReaderOrErr = SampleProfileReader::create(BaseFilename, Context); 1811*540489deSweihe if (std::error_code EC = BaseReaderOrErr.getError()) 1812*540489deSweihe exitWithErrorCode(EC, BaseFilename); 1813*540489deSweihe 1814*540489deSweihe auto TestReaderOrErr = SampleProfileReader::create(TestFilename, Context); 1815*540489deSweihe if (std::error_code EC = TestReaderOrErr.getError()) 1816*540489deSweihe exitWithErrorCode(EC, TestFilename); 1817*540489deSweihe 1818*540489deSweihe BaseReader = std::move(BaseReaderOrErr.get()); 1819*540489deSweihe TestReader = std::move(TestReaderOrErr.get()); 1820*540489deSweihe 1821*540489deSweihe if (std::error_code EC = BaseReader->read()) 1822*540489deSweihe exitWithErrorCode(EC, BaseFilename); 1823*540489deSweihe if (std::error_code EC = TestReader->read()) 1824*540489deSweihe exitWithErrorCode(EC, TestFilename); 1825*540489deSweihe 1826*540489deSweihe // Load BaseHotThreshold and TestHotThreshold as 99-percentile threshold in 1827*540489deSweihe // profile summary. 1828*540489deSweihe const uint64_t HotCutoff = 990000; 1829*540489deSweihe ProfileSummary &BasePS = BaseReader->getSummary(); 1830*540489deSweihe for (const auto &SummaryEntry : BasePS.getDetailedSummary()) { 1831*540489deSweihe if (SummaryEntry.Cutoff == HotCutoff) { 1832*540489deSweihe BaseHotThreshold = SummaryEntry.MinCount; 1833*540489deSweihe break; 1834*540489deSweihe } 1835*540489deSweihe } 1836*540489deSweihe 1837*540489deSweihe ProfileSummary &TestPS = TestReader->getSummary(); 1838*540489deSweihe for (const auto &SummaryEntry : TestPS.getDetailedSummary()) { 1839*540489deSweihe if (SummaryEntry.Cutoff == HotCutoff) { 1840*540489deSweihe TestHotThreshold = SummaryEntry.MinCount; 1841*540489deSweihe break; 1842*540489deSweihe } 1843*540489deSweihe } 1844*540489deSweihe return std::error_code(); 1845*540489deSweihe } 1846*540489deSweihe 1847*540489deSweihe void overlapSampleProfile(const std::string &BaseFilename, 1848*540489deSweihe const std::string &TestFilename, 1849*540489deSweihe const OverlapFuncFilters &FuncFilter, 1850*540489deSweihe uint64_t SimilarityCutoff, raw_fd_ostream &OS) { 1851*540489deSweihe using namespace sampleprof; 1852*540489deSweihe 1853*540489deSweihe // We use 0.000005 to initialize OverlapAggr.Epsilon because the final metrics 1854*540489deSweihe // report 2--3 places after decimal point in percentage numbers. 1855*540489deSweihe SampleOverlapAggregator OverlapAggr( 1856*540489deSweihe BaseFilename, TestFilename, 1857*540489deSweihe static_cast<double>(SimilarityCutoff) / 1000000, 0.000005, FuncFilter); 1858*540489deSweihe if (std::error_code EC = OverlapAggr.loadProfiles()) 1859*540489deSweihe exitWithErrorCode(EC); 1860*540489deSweihe 1861*540489deSweihe OverlapAggr.initializeSampleProfileOverlap(); 1862*540489deSweihe if (OverlapAggr.detectZeroSampleProfile(OS)) 1863*540489deSweihe return; 1864*540489deSweihe 1865*540489deSweihe OverlapAggr.computeSampleProfileOverlap(OS); 1866*540489deSweihe 1867*540489deSweihe OverlapAggr.dumpProgramSummary(OS); 1868*540489deSweihe OverlapAggr.dumpHotFuncAndBlockOverlap(OS); 1869*540489deSweihe OverlapAggr.dumpFuncSimilarity(OS); 1870*540489deSweihe } 1871*540489deSweihe 1872998b97f6SRong Xu static int overlap_main(int argc, const char *argv[]) { 1873998b97f6SRong Xu cl::opt<std::string> BaseFilename(cl::Positional, cl::Required, 1874998b97f6SRong Xu cl::desc("<base profile file>")); 1875998b97f6SRong Xu cl::opt<std::string> TestFilename(cl::Positional, cl::Required, 1876998b97f6SRong Xu cl::desc("<test profile file>")); 1877998b97f6SRong Xu cl::opt<std::string> Output("output", cl::value_desc("output"), cl::init("-"), 1878998b97f6SRong Xu cl::desc("Output file")); 1879998b97f6SRong Xu cl::alias OutputA("o", cl::desc("Alias for --output"), cl::aliasopt(Output)); 1880998b97f6SRong Xu cl::opt<bool> IsCS("cs", cl::init(false), 1881998b97f6SRong Xu cl::desc("For context sensitive counts")); 1882998b97f6SRong Xu cl::opt<unsigned long long> ValueCutoff( 1883998b97f6SRong Xu "value-cutoff", cl::init(-1), 1884998b97f6SRong Xu cl::desc( 1885998b97f6SRong Xu "Function level overlap information for every function in test " 1886998b97f6SRong Xu "profile with max count value greater then the parameter value")); 1887998b97f6SRong Xu cl::opt<std::string> FuncNameFilter( 1888998b97f6SRong Xu "function", 1889998b97f6SRong Xu cl::desc("Function level overlap information for matching functions")); 1890*540489deSweihe cl::opt<unsigned long long> SimilarityCutoff( 1891*540489deSweihe "similarity-cutoff", cl::init(0), 1892*540489deSweihe cl::desc( 1893*540489deSweihe "For sample profiles, list function names for overlapped functions " 1894*540489deSweihe "with similarities below the cutoff (percentage times 10000).")); 1895*540489deSweihe cl::opt<ProfileKinds> ProfileKind( 1896*540489deSweihe cl::desc("Profile kind:"), cl::init(instr), 1897*540489deSweihe cl::values(clEnumVal(instr, "Instrumentation profile (default)"), 1898*540489deSweihe clEnumVal(sample, "Sample profile"))); 1899998b97f6SRong Xu cl::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n"); 1900998b97f6SRong Xu 1901998b97f6SRong Xu std::error_code EC; 1902d9b948b6SFangrui Song raw_fd_ostream OS(Output.data(), EC, sys::fs::OF_Text); 1903998b97f6SRong Xu if (EC) 1904998b97f6SRong Xu exitWithErrorCode(EC, Output); 1905998b97f6SRong Xu 1906*540489deSweihe if (ProfileKind == instr) 1907998b97f6SRong Xu overlapInstrProfile(BaseFilename, TestFilename, 1908998b97f6SRong Xu OverlapFuncFilters{ValueCutoff, FuncNameFilter}, OS, 1909998b97f6SRong Xu IsCS); 1910*540489deSweihe else 1911*540489deSweihe overlapSampleProfile(BaseFilename, TestFilename, 1912*540489deSweihe OverlapFuncFilters{ValueCutoff, FuncNameFilter}, 1913*540489deSweihe SimilarityCutoff, OS); 1914998b97f6SRong Xu 1915998b97f6SRong Xu return 0; 1916998b97f6SRong Xu } 1917998b97f6SRong Xu 19180cf1f56aSRong Xu typedef struct ValueSitesStats { 19190cf1f56aSRong Xu ValueSitesStats() 19200cf1f56aSRong Xu : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0), 19210cf1f56aSRong Xu TotalNumValues(0) {} 19220cf1f56aSRong Xu uint64_t TotalNumValueSites; 19230cf1f56aSRong Xu uint64_t TotalNumValueSitesWithValueProfile; 19240cf1f56aSRong Xu uint64_t TotalNumValues; 19250cf1f56aSRong Xu std::vector<unsigned> ValueSitesHistogram; 19260cf1f56aSRong Xu } ValueSitesStats; 19270cf1f56aSRong Xu 19280cf1f56aSRong Xu static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK, 19290cf1f56aSRong Xu ValueSitesStats &Stats, raw_fd_ostream &OS, 193060faea19SRong Xu InstrProfSymtab *Symtab) { 19310cf1f56aSRong Xu uint32_t NS = Func.getNumValueSites(VK); 19320cf1f56aSRong Xu Stats.TotalNumValueSites += NS; 19330cf1f56aSRong Xu for (size_t I = 0; I < NS; ++I) { 19340cf1f56aSRong Xu uint32_t NV = Func.getNumValueDataForSite(VK, I); 19350cf1f56aSRong Xu std::unique_ptr<InstrProfValueData[]> VD = Func.getValueForSite(VK, I); 19360cf1f56aSRong Xu Stats.TotalNumValues += NV; 19370cf1f56aSRong Xu if (NV) { 19380cf1f56aSRong Xu Stats.TotalNumValueSitesWithValueProfile++; 19390cf1f56aSRong Xu if (NV > Stats.ValueSitesHistogram.size()) 19400cf1f56aSRong Xu Stats.ValueSitesHistogram.resize(NV, 0); 19410cf1f56aSRong Xu Stats.ValueSitesHistogram[NV - 1]++; 19420cf1f56aSRong Xu } 194352aa224aSRong Xu 194452aa224aSRong Xu uint64_t SiteSum = 0; 194552aa224aSRong Xu for (uint32_t V = 0; V < NV; V++) 194652aa224aSRong Xu SiteSum += VD[V].Count; 194752aa224aSRong Xu if (SiteSum == 0) 194852aa224aSRong Xu SiteSum = 1; 194952aa224aSRong Xu 19500cf1f56aSRong Xu for (uint32_t V = 0; V < NV; V++) { 195152aa224aSRong Xu OS << "\t[ " << format("%2u", I) << ", "; 195260faea19SRong Xu if (Symtab == nullptr) 195340a7f63cSPetar Jovanovic OS << format("%4" PRIu64, VD[V].Value); 195460faea19SRong Xu else 195560faea19SRong Xu OS << Symtab->getFuncName(VD[V].Value); 195652aa224aSRong Xu OS << ", " << format("%10" PRId64, VD[V].Count) << " ] (" 195752aa224aSRong Xu << format("%.2f%%", (VD[V].Count * 100.0 / SiteSum)) << ")\n"; 19580cf1f56aSRong Xu } 19590cf1f56aSRong Xu } 19600cf1f56aSRong Xu } 19610cf1f56aSRong Xu 19620cf1f56aSRong Xu static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK, 19630cf1f56aSRong Xu ValueSitesStats &Stats) { 19640cf1f56aSRong Xu OS << " Total number of sites: " << Stats.TotalNumValueSites << "\n"; 19650cf1f56aSRong Xu OS << " Total number of sites with values: " 19660cf1f56aSRong Xu << Stats.TotalNumValueSitesWithValueProfile << "\n"; 19670cf1f56aSRong Xu OS << " Total number of profiled values: " << Stats.TotalNumValues << "\n"; 19680cf1f56aSRong Xu 19690cf1f56aSRong Xu OS << " Value sites histogram:\n\tNumTargets, SiteCount\n"; 19700cf1f56aSRong Xu for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) { 19710cf1f56aSRong Xu if (Stats.ValueSitesHistogram[I] > 0) 19720cf1f56aSRong Xu OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n"; 19730cf1f56aSRong Xu } 19740cf1f56aSRong Xu } 19750cf1f56aSRong Xu 19761afc1de4SBenjamin Kramer static int showInstrProfile(const std::string &Filename, bool ShowCounts, 1977801b5319SXinliang David Li uint32_t TopN, bool ShowIndirectCallTargets, 1978801b5319SXinliang David Li bool ShowMemOPSizes, bool ShowDetailedSummary, 1979183ebbe0SEaswaran Raman std::vector<uint32_t> DetailedSummaryCutoffs, 1980a6ff69f6SRong Xu bool ShowAllFunctions, bool ShowCS, 1981a6ff69f6SRong Xu uint64_t ValueCutoff, bool OnlyListBelow, 1982a6ff69f6SRong Xu const std::string &ShowFunction, bool TextFormat, 1983a6ff69f6SRong Xu raw_fd_ostream &OS) { 1984fcd55607SDiego Novillo auto ReaderOrErr = InstrProfReader::create(Filename); 19851afc1de4SBenjamin Kramer std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs); 19861afc1de4SBenjamin Kramer if (ShowDetailedSummary && Cutoffs.empty()) { 1987183ebbe0SEaswaran Raman Cutoffs = {800000, 900000, 950000, 990000, 999000, 999900, 999990}; 1988183ebbe0SEaswaran Raman } 19891afc1de4SBenjamin Kramer InstrProfSummaryBuilder Builder(std::move(Cutoffs)); 19909152fd17SVedant Kumar if (Error E = ReaderOrErr.takeError()) 19919152fd17SVedant Kumar exitWithError(std::move(E), Filename); 19929af28ef9SJustin Bogner 1993fcd55607SDiego Novillo auto Reader = std::move(ReaderOrErr.get()); 199433c76c0cSRong Xu bool IsIRInstr = Reader->isIRLevelProfile(); 1995183ebbe0SEaswaran Raman size_t ShownFunctions = 0; 199652aa224aSRong Xu size_t BelowCutoffFunctions = 0; 19970cf1f56aSRong Xu int NumVPKind = IPVK_Last - IPVK_First + 1; 19980cf1f56aSRong Xu std::vector<ValueSitesStats> VPStats(NumVPKind); 1999801b5319SXinliang David Li 2000801b5319SXinliang David Li auto MinCmp = [](const std::pair<std::string, uint64_t> &v1, 2001801b5319SXinliang David Li const std::pair<std::string, uint64_t> &v2) { 2002801b5319SXinliang David Li return v1.second > v2.second; 2003801b5319SXinliang David Li }; 2004801b5319SXinliang David Li 2005801b5319SXinliang David Li std::priority_queue<std::pair<std::string, uint64_t>, 2006801b5319SXinliang David Li std::vector<std::pair<std::string, uint64_t>>, 2007801b5319SXinliang David Li decltype(MinCmp)> 2008801b5319SXinliang David Li HottestFuncs(MinCmp); 2009801b5319SXinliang David Li 201052aa224aSRong Xu if (!TextFormat && OnlyListBelow) { 201152aa224aSRong Xu OS << "The list of functions with the maximum counter less than " 201252aa224aSRong Xu << ValueCutoff << ":\n"; 201352aa224aSRong Xu } 201452aa224aSRong Xu 2015c6ba9ca1SRichard Smith // Add marker so that IR-level instrumentation round-trips properly. 2016c6ba9ca1SRichard Smith if (TextFormat && IsIRInstr) 2017c6ba9ca1SRichard Smith OS << ":ir\n"; 2018c6ba9ca1SRichard Smith 20199af28ef9SJustin Bogner for (const auto &Func : *Reader) { 2020a6ff69f6SRong Xu if (Reader->isIRLevelProfile()) { 2021a6ff69f6SRong Xu bool FuncIsCS = NamedInstrProfRecord::hasCSFlagInHash(Func.Hash); 2022a6ff69f6SRong Xu if (FuncIsCS != ShowCS) 2023a6ff69f6SRong Xu continue; 2024a6ff69f6SRong Xu } 2025d5336ae2SDiego Novillo bool Show = 2026d5336ae2SDiego Novillo ShowAllFunctions || (!ShowFunction.empty() && 20279af28ef9SJustin Bogner Func.Name.find(ShowFunction) != Func.Name.npos); 20289af28ef9SJustin Bogner 2029c6ba9ca1SRichard Smith bool doTextFormatDump = (Show && TextFormat); 20306f7c19a4SXinliang David Li 20316f7c19a4SXinliang David Li if (doTextFormatDump) { 2032a716cc5cSXinliang David Li InstrProfSymtab &Symtab = Reader->getSymtab(); 2033cf9d52c6SDavid Blaikie InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab, 2034cf9d52c6SDavid Blaikie OS); 20356f7c19a4SXinliang David Li continue; 20366f7c19a4SXinliang David Li } 20376f7c19a4SXinliang David Li 2038b59d7c73SJustin Bogner assert(Func.Counts.size() > 0 && "function missing entry counter"); 2039e5a17e3fSEaswaran Raman Builder.addRecord(Func); 20406f7c19a4SXinliang David Li 20417162e16eSRong Xu uint64_t FuncMax = 0; 204252aa224aSRong Xu uint64_t FuncSum = 0; 204352aa224aSRong Xu for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) { 2044a23f6234SWei Mi if (Func.Counts[I] == (uint64_t)-1) 2045a23f6234SWei Mi continue; 20467162e16eSRong Xu FuncMax = std::max(FuncMax, Func.Counts[I]); 204752aa224aSRong Xu FuncSum += Func.Counts[I]; 204852aa224aSRong Xu } 20497162e16eSRong Xu 205052aa224aSRong Xu if (FuncMax < ValueCutoff) { 205152aa224aSRong Xu ++BelowCutoffFunctions; 205252aa224aSRong Xu if (OnlyListBelow) { 205352aa224aSRong Xu OS << " " << Func.Name << ": (Max = " << FuncMax 205452aa224aSRong Xu << " Sum = " << FuncSum << ")\n"; 205552aa224aSRong Xu } 205652aa224aSRong Xu continue; 205752aa224aSRong Xu } else if (OnlyListBelow) 205852aa224aSRong Xu continue; 205952aa224aSRong Xu 206052aa224aSRong Xu if (TopN) { 2061801b5319SXinliang David Li if (HottestFuncs.size() == TopN) { 2062801b5319SXinliang David Li if (HottestFuncs.top().second < FuncMax) { 2063801b5319SXinliang David Li HottestFuncs.pop(); 2064801b5319SXinliang David Li HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); 2065801b5319SXinliang David Li } 2066801b5319SXinliang David Li } else 2067801b5319SXinliang David Li HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); 2068801b5319SXinliang David Li } 2069801b5319SXinliang David Li 20709af28ef9SJustin Bogner if (Show) { 20719af28ef9SJustin Bogner if (!ShownFunctions) 20729af28ef9SJustin Bogner OS << "Counters:\n"; 20736f7c19a4SXinliang David Li 20749af28ef9SJustin Bogner ++ShownFunctions; 20759af28ef9SJustin Bogner 20769af28ef9SJustin Bogner OS << " " << Func.Name << ":\n" 2077423380f9SJustin Bogner << " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n" 207833c76c0cSRong Xu << " Counters: " << Func.Counts.size() << "\n"; 207933c76c0cSRong Xu if (!IsIRInstr) 208033c76c0cSRong Xu OS << " Function count: " << Func.Counts[0] << "\n"; 20816f7c19a4SXinliang David Li 20829e9a057aSJustin Bogner if (ShowIndirectCallTargets) 20832004f003SXinliang David Li OS << " Indirect Call Site Count: " 20842004f003SXinliang David Li << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n"; 20859af28ef9SJustin Bogner 208660faea19SRong Xu uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize); 208760faea19SRong Xu if (ShowMemOPSizes && NumMemOPCalls > 0) 208860faea19SRong Xu OS << " Number of Memory Intrinsics Calls: " << NumMemOPCalls 208960faea19SRong Xu << "\n"; 209060faea19SRong Xu 20916f7c19a4SXinliang David Li if (ShowCounts) { 20929af28ef9SJustin Bogner OS << " Block counts: ["; 209333c76c0cSRong Xu size_t Start = (IsIRInstr ? 0 : 1); 209433c76c0cSRong Xu for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) { 209533c76c0cSRong Xu OS << (I == Start ? "" : ", ") << Func.Counts[I]; 20969af28ef9SJustin Bogner } 20979af28ef9SJustin Bogner OS << "]\n"; 20986f7c19a4SXinliang David Li } 20999e9a057aSJustin Bogner 21006f7c19a4SXinliang David Li if (ShowIndirectCallTargets) { 21019e9a057aSJustin Bogner OS << " Indirect Target Results:\n"; 21020cf1f56aSRong Xu traverseAllValueSites(Func, IPVK_IndirectCallTarget, 21030cf1f56aSRong Xu VPStats[IPVK_IndirectCallTarget], OS, 210460faea19SRong Xu &(Reader->getSymtab())); 210560faea19SRong Xu } 210660faea19SRong Xu 210760faea19SRong Xu if (ShowMemOPSizes && NumMemOPCalls > 0) { 2108cd2aa0d2STeresa Johnson OS << " Memory Intrinsic Size Results:\n"; 210960faea19SRong Xu traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS, 211060faea19SRong Xu nullptr); 21119e9a057aSJustin Bogner } 21129af28ef9SJustin Bogner } 21136f7c19a4SXinliang David Li } 2114db1225d0SJustin Bogner if (Reader->hasError()) 21159152fd17SVedant Kumar exitWithError(Reader->getError(), Filename); 21169af28ef9SJustin Bogner 2117c6ba9ca1SRichard Smith if (TextFormat) 21186f7c19a4SXinliang David Li return 0; 21197cefdb81SEaswaran Raman std::unique_ptr<ProfileSummary> PS(Builder.getSummary()); 212050da55a5SRong Xu bool IsIR = Reader->isIRLevelProfile(); 212150da55a5SRong Xu OS << "Instrumentation level: " << (IsIR ? "IR" : "Front-end"); 212250da55a5SRong Xu if (IsIR) 212350da55a5SRong Xu OS << " entry_first = " << Reader->instrEntryBBEnabled(); 212450da55a5SRong Xu OS << "\n"; 21259af28ef9SJustin Bogner if (ShowAllFunctions || !ShowFunction.empty()) 21269af28ef9SJustin Bogner OS << "Functions shown: " << ShownFunctions << "\n"; 2127e5a17e3fSEaswaran Raman OS << "Total functions: " << PS->getNumFunctions() << "\n"; 212852aa224aSRong Xu if (ValueCutoff > 0) { 212952aa224aSRong Xu OS << "Number of functions with maximum count (< " << ValueCutoff 213052aa224aSRong Xu << "): " << BelowCutoffFunctions << "\n"; 213152aa224aSRong Xu OS << "Number of functions with maximum count (>= " << ValueCutoff 213252aa224aSRong Xu << "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n"; 213352aa224aSRong Xu } 2134e5a17e3fSEaswaran Raman OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n"; 21357cefdb81SEaswaran Raman OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n"; 213660faea19SRong Xu 2137801b5319SXinliang David Li if (TopN) { 2138801b5319SXinliang David Li std::vector<std::pair<std::string, uint64_t>> SortedHottestFuncs; 2139801b5319SXinliang David Li while (!HottestFuncs.empty()) { 2140801b5319SXinliang David Li SortedHottestFuncs.emplace_back(HottestFuncs.top()); 2141801b5319SXinliang David Li HottestFuncs.pop(); 2142801b5319SXinliang David Li } 2143801b5319SXinliang David Li OS << "Top " << TopN 2144801b5319SXinliang David Li << " functions with the largest internal block counts: \n"; 2145801b5319SXinliang David Li for (auto &hotfunc : llvm::reverse(SortedHottestFuncs)) 2146801b5319SXinliang David Li OS << " " << hotfunc.first << ", max count = " << hotfunc.second << "\n"; 2147801b5319SXinliang David Li } 2148801b5319SXinliang David Li 2149872362c4SXinliang David Li if (ShownFunctions && ShowIndirectCallTargets) { 21500cf1f56aSRong Xu OS << "Statistics for indirect call sites profile:\n"; 21510cf1f56aSRong Xu showValueSitesStats(OS, IPVK_IndirectCallTarget, 21520cf1f56aSRong Xu VPStats[IPVK_IndirectCallTarget]); 2153872362c4SXinliang David Li } 2154183ebbe0SEaswaran Raman 215560faea19SRong Xu if (ShownFunctions && ShowMemOPSizes) { 215660faea19SRong Xu OS << "Statistics for memory intrinsic calls sizes profile:\n"; 215760faea19SRong Xu showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]); 215860faea19SRong Xu } 215960faea19SRong Xu 2160183ebbe0SEaswaran Raman if (ShowDetailedSummary) { 21617cefdb81SEaswaran Raman OS << "Total number of blocks: " << PS->getNumCounts() << "\n"; 2162e5a17e3fSEaswaran Raman OS << "Total count: " << PS->getTotalCount() << "\n"; 216317fc6518SWenlei He PS->printDetailedSummary(OS); 2164183ebbe0SEaswaran Raman } 21659af28ef9SJustin Bogner return 0; 21669af28ef9SJustin Bogner } 21679af28ef9SJustin Bogner 2168eee532cdSWei Mi static void showSectionInfo(sampleprof::SampleProfileReader *Reader, 2169eee532cdSWei Mi raw_fd_ostream &OS) { 2170eee532cdSWei Mi if (!Reader->dumpSectionInfo(OS)) { 2171eee532cdSWei Mi WithColor::warning() << "-show-sec-info-only is only supported for " 2172eee532cdSWei Mi << "sample profile in extbinary format and is " 2173eee532cdSWei Mi << "ignored for other formats.\n"; 2174eee532cdSWei Mi return; 2175eee532cdSWei Mi } 2176eee532cdSWei Mi } 2177eee532cdSWei Mi 2178546be088SFangrui Song namespace { 217953cf5302Sweihe struct HotFuncInfo { 218053cf5302Sweihe StringRef FuncName; 218153cf5302Sweihe uint64_t TotalCount; 218253cf5302Sweihe double TotalCountPercent; 218353cf5302Sweihe uint64_t MaxCount; 218453cf5302Sweihe uint64_t EntryCount; 218553cf5302Sweihe 218653cf5302Sweihe HotFuncInfo() 218753cf5302Sweihe : FuncName(), TotalCount(0), TotalCountPercent(0.0f), MaxCount(0), 218853cf5302Sweihe EntryCount(0) {} 218953cf5302Sweihe 219053cf5302Sweihe HotFuncInfo(StringRef FN, uint64_t TS, double TSP, uint64_t MS, uint64_t ES) 219153cf5302Sweihe : FuncName(FN), TotalCount(TS), TotalCountPercent(TSP), MaxCount(MS), 219253cf5302Sweihe EntryCount(ES) {} 219353cf5302Sweihe }; 2194546be088SFangrui Song } // namespace 219553cf5302Sweihe 219653cf5302Sweihe // Print out detailed information about hot functions in PrintValues vector. 219753cf5302Sweihe // Users specify titles and offset of every columns through ColumnTitle and 219853cf5302Sweihe // ColumnOffset. The size of ColumnTitle and ColumnOffset need to be the same 219953cf5302Sweihe // and at least 4. Besides, users can optionally give a HotFuncMetric string to 220053cf5302Sweihe // print out or let it be an empty string. 220153cf5302Sweihe static void dumpHotFunctionList(const std::vector<std::string> &ColumnTitle, 220253cf5302Sweihe const std::vector<int> &ColumnOffset, 220353cf5302Sweihe const std::vector<HotFuncInfo> &PrintValues, 220453cf5302Sweihe uint64_t HotFuncCount, uint64_t TotalFuncCount, 220553cf5302Sweihe uint64_t HotProfCount, uint64_t TotalProfCount, 220653cf5302Sweihe const std::string &HotFuncMetric, 220753cf5302Sweihe raw_fd_ostream &OS) { 2208*540489deSweihe assert(ColumnOffset.size() == ColumnTitle.size() && 2209*540489deSweihe "ColumnOffset and ColumnTitle should have the same size"); 2210*540489deSweihe assert(ColumnTitle.size() >= 4 && 2211*540489deSweihe "ColumnTitle should have at least 4 elements"); 2212*540489deSweihe assert(TotalFuncCount > 0 && 2213*540489deSweihe "There should be at least one function in the profile"); 221453cf5302Sweihe double TotalProfPercent = 0; 221553cf5302Sweihe if (TotalProfCount > 0) 2216*540489deSweihe TotalProfPercent = static_cast<double>(HotProfCount) / TotalProfCount * 100; 221753cf5302Sweihe 221853cf5302Sweihe formatted_raw_ostream FOS(OS); 221953cf5302Sweihe FOS << HotFuncCount << " out of " << TotalFuncCount 222053cf5302Sweihe << " functions with profile (" 2221*540489deSweihe << format("%.2f%%", 2222*540489deSweihe (static_cast<double>(HotFuncCount) / TotalFuncCount * 100)) 222353cf5302Sweihe << ") are considered hot functions"; 222453cf5302Sweihe if (!HotFuncMetric.empty()) 222553cf5302Sweihe FOS << " (" << HotFuncMetric << ")"; 222653cf5302Sweihe FOS << ".\n"; 222753cf5302Sweihe FOS << HotProfCount << " out of " << TotalProfCount << " profile counts (" 222853cf5302Sweihe << format("%.2f%%", TotalProfPercent) << ") are from hot functions.\n"; 222953cf5302Sweihe 223053cf5302Sweihe for (size_t I = 0; I < ColumnTitle.size(); ++I) { 223153cf5302Sweihe FOS.PadToColumn(ColumnOffset[I]); 223253cf5302Sweihe FOS << ColumnTitle[I]; 223353cf5302Sweihe } 223453cf5302Sweihe FOS << "\n"; 223553cf5302Sweihe 2236546be088SFangrui Song for (const HotFuncInfo &R : PrintValues) { 223753cf5302Sweihe FOS.PadToColumn(ColumnOffset[0]); 223853cf5302Sweihe FOS << R.TotalCount << " (" << format("%.2f%%", R.TotalCountPercent) << ")"; 223953cf5302Sweihe FOS.PadToColumn(ColumnOffset[1]); 224053cf5302Sweihe FOS << R.MaxCount; 224153cf5302Sweihe FOS.PadToColumn(ColumnOffset[2]); 224253cf5302Sweihe FOS << R.EntryCount; 224353cf5302Sweihe FOS.PadToColumn(ColumnOffset[3]); 224453cf5302Sweihe FOS << R.FuncName << "\n"; 224553cf5302Sweihe } 224653cf5302Sweihe return; 224753cf5302Sweihe } 224853cf5302Sweihe 224953cf5302Sweihe static int 225053cf5302Sweihe showHotFunctionList(const StringMap<sampleprof::FunctionSamples> &Profiles, 225153cf5302Sweihe ProfileSummary &PS, raw_fd_ostream &OS) { 225253cf5302Sweihe using namespace sampleprof; 225353cf5302Sweihe 225453cf5302Sweihe const uint32_t HotFuncCutoff = 990000; 225553cf5302Sweihe auto &SummaryVector = PS.getDetailedSummary(); 225653cf5302Sweihe uint64_t MinCountThreshold = 0; 2257546be088SFangrui Song for (const ProfileSummaryEntry &SummaryEntry : SummaryVector) { 225853cf5302Sweihe if (SummaryEntry.Cutoff == HotFuncCutoff) { 225953cf5302Sweihe MinCountThreshold = SummaryEntry.MinCount; 226053cf5302Sweihe break; 226153cf5302Sweihe } 226253cf5302Sweihe } 226353cf5302Sweihe 226453cf5302Sweihe // Traverse all functions in the profile and keep only hot functions. 226553cf5302Sweihe // The following loop also calculates the sum of total samples of all 226653cf5302Sweihe // functions. 226753cf5302Sweihe std::multimap<uint64_t, std::pair<const FunctionSamples *, const uint64_t>, 226853cf5302Sweihe std::greater<uint64_t>> 226953cf5302Sweihe HotFunc; 227053cf5302Sweihe uint64_t ProfileTotalSample = 0; 227153cf5302Sweihe uint64_t HotFuncSample = 0; 227253cf5302Sweihe uint64_t HotFuncCount = 0; 2273*540489deSweihe 227453cf5302Sweihe for (const auto &I : Profiles) { 2275*540489deSweihe FuncSampleStats FuncStats; 2276546be088SFangrui Song const FunctionSamples &FuncProf = I.second; 227753cf5302Sweihe ProfileTotalSample += FuncProf.getTotalSamples(); 2278*540489deSweihe getFuncSampleStats(FuncProf, FuncStats, MinCountThreshold); 227953cf5302Sweihe 2280*540489deSweihe if (isFunctionHot(FuncStats, MinCountThreshold)) { 228153cf5302Sweihe HotFunc.emplace(FuncProf.getTotalSamples(), 2282*540489deSweihe std::make_pair(&(I.second), FuncStats.MaxSample)); 228353cf5302Sweihe HotFuncSample += FuncProf.getTotalSamples(); 228453cf5302Sweihe ++HotFuncCount; 228553cf5302Sweihe } 228653cf5302Sweihe } 228753cf5302Sweihe 228853cf5302Sweihe std::vector<std::string> ColumnTitle{"Total sample (%)", "Max sample", 228953cf5302Sweihe "Entry sample", "Function name"}; 229053cf5302Sweihe std::vector<int> ColumnOffset{0, 24, 42, 58}; 229153cf5302Sweihe std::string Metric = 229253cf5302Sweihe std::string("max sample >= ") + std::to_string(MinCountThreshold); 229353cf5302Sweihe std::vector<HotFuncInfo> PrintValues; 229453cf5302Sweihe for (const auto &FuncPair : HotFunc) { 2295546be088SFangrui Song const FunctionSamples &Func = *FuncPair.second.first; 229653cf5302Sweihe double TotalSamplePercent = 229753cf5302Sweihe (ProfileTotalSample > 0) 2298546be088SFangrui Song ? (Func.getTotalSamples() * 100.0) / ProfileTotalSample 229953cf5302Sweihe : 0; 230053cf5302Sweihe PrintValues.emplace_back(HotFuncInfo( 2301546be088SFangrui Song Func.getFuncName(), Func.getTotalSamples(), TotalSamplePercent, 2302546be088SFangrui Song FuncPair.second.second, Func.getEntrySamples())); 230353cf5302Sweihe } 230453cf5302Sweihe dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount, 230553cf5302Sweihe Profiles.size(), HotFuncSample, ProfileTotalSample, 230653cf5302Sweihe Metric, OS); 230753cf5302Sweihe 230853cf5302Sweihe return 0; 230953cf5302Sweihe } 231053cf5302Sweihe 23111afc1de4SBenjamin Kramer static int showSampleProfile(const std::string &Filename, bool ShowCounts, 231217fc6518SWenlei He bool ShowAllFunctions, bool ShowDetailedSummary, 23131afc1de4SBenjamin Kramer const std::string &ShowFunction, 2314eee532cdSWei Mi bool ShowProfileSymbolList, 231553cf5302Sweihe bool ShowSectionInfoOnly, bool ShowHotFuncList, 231653cf5302Sweihe raw_fd_ostream &OS) { 2317d5336ae2SDiego Novillo using namespace sampleprof; 231803b42e41SMehdi Amini LLVMContext Context; 231903b42e41SMehdi Amini auto ReaderOrErr = SampleProfileReader::create(Filename, Context); 2320fcd55607SDiego Novillo if (std::error_code EC = ReaderOrErr.getError()) 23214f823667SNathan Slingerland exitWithErrorCode(EC, Filename); 2322d5336ae2SDiego Novillo 2323fcd55607SDiego Novillo auto Reader = std::move(ReaderOrErr.get()); 2324eee532cdSWei Mi 2325eee532cdSWei Mi if (ShowSectionInfoOnly) { 2326eee532cdSWei Mi showSectionInfo(Reader.get(), OS); 2327eee532cdSWei Mi return 0; 2328eee532cdSWei Mi } 2329eee532cdSWei Mi 2330c6d032abSDiego Novillo if (std::error_code EC = Reader->read()) 23314f823667SNathan Slingerland exitWithErrorCode(EC, Filename); 2332c6d032abSDiego Novillo 2333d5336ae2SDiego Novillo if (ShowAllFunctions || ShowFunction.empty()) 2334d5336ae2SDiego Novillo Reader->dump(OS); 2335d5336ae2SDiego Novillo else 2336d5336ae2SDiego Novillo Reader->dumpFunctionProfile(ShowFunction, OS); 2337d5336ae2SDiego Novillo 2338798e59b8SWei Mi if (ShowProfileSymbolList) { 2339798e59b8SWei Mi std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList = 2340798e59b8SWei Mi Reader->getProfileSymbolList(); 2341798e59b8SWei Mi ReaderList->dump(OS); 2342798e59b8SWei Mi } 2343798e59b8SWei Mi 234417fc6518SWenlei He if (ShowDetailedSummary) { 234517fc6518SWenlei He auto &PS = Reader->getSummary(); 234617fc6518SWenlei He PS.printSummary(OS); 234717fc6518SWenlei He PS.printDetailedSummary(OS); 234817fc6518SWenlei He } 234917fc6518SWenlei He 235053cf5302Sweihe if (ShowHotFuncList) 235153cf5302Sweihe showHotFunctionList(Reader->getProfiles(), Reader->getSummary(), OS); 235253cf5302Sweihe 2353d5336ae2SDiego Novillo return 0; 2354d5336ae2SDiego Novillo } 2355d5336ae2SDiego Novillo 2356f044d3f9SBenjamin Kramer static int show_main(int argc, const char *argv[]) { 2357d5336ae2SDiego Novillo cl::opt<std::string> Filename(cl::Positional, cl::Required, 2358d5336ae2SDiego Novillo cl::desc("<profdata-file>")); 2359d5336ae2SDiego Novillo 2360d5336ae2SDiego Novillo cl::opt<bool> ShowCounts("counts", cl::init(false), 2361d5336ae2SDiego Novillo cl::desc("Show counter values for shown functions")); 23626f7c19a4SXinliang David Li cl::opt<bool> TextFormat( 23636f7c19a4SXinliang David Li "text", cl::init(false), 23646f7c19a4SXinliang David Li cl::desc("Show instr profile data in text dump format")); 23659e9a057aSJustin Bogner cl::opt<bool> ShowIndirectCallTargets( 23669e9a057aSJustin Bogner "ic-targets", cl::init(false), 23679e9a057aSJustin Bogner cl::desc("Show indirect call site target values for shown functions")); 236860faea19SRong Xu cl::opt<bool> ShowMemOPSizes( 236960faea19SRong Xu "memop-sizes", cl::init(false), 237060faea19SRong Xu cl::desc("Show the profiled sizes of the memory intrinsic calls " 237160faea19SRong Xu "for shown functions")); 2372183ebbe0SEaswaran Raman cl::opt<bool> ShowDetailedSummary("detailed-summary", cl::init(false), 2373183ebbe0SEaswaran Raman cl::desc("Show detailed profile summary")); 2374183ebbe0SEaswaran Raman cl::list<uint32_t> DetailedSummaryCutoffs( 2375183ebbe0SEaswaran Raman cl::CommaSeparated, "detailed-summary-cutoffs", 2376183ebbe0SEaswaran Raman cl::desc( 2377183ebbe0SEaswaran Raman "Cutoff percentages (times 10000) for generating detailed summary"), 2378183ebbe0SEaswaran Raman cl::value_desc("800000,901000,999999")); 237953cf5302Sweihe cl::opt<bool> ShowHotFuncList( 238053cf5302Sweihe "hot-func-list", cl::init(false), 238153cf5302Sweihe cl::desc("Show profile summary of a list of hot functions")); 2382d5336ae2SDiego Novillo cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false), 2383d5336ae2SDiego Novillo cl::desc("Details for every function")); 2384a6ff69f6SRong Xu cl::opt<bool> ShowCS("showcs", cl::init(false), 2385a6ff69f6SRong Xu cl::desc("Show context sensitive counts")); 2386d5336ae2SDiego Novillo cl::opt<std::string> ShowFunction("function", 2387d5336ae2SDiego Novillo cl::desc("Details for matching functions")); 2388d5336ae2SDiego Novillo 2389d5336ae2SDiego Novillo cl::opt<std::string> OutputFilename("output", cl::value_desc("output"), 2390d5336ae2SDiego Novillo cl::init("-"), cl::desc("Output file")); 2391d5336ae2SDiego Novillo cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), 2392d5336ae2SDiego Novillo cl::aliasopt(OutputFilename)); 2393d5336ae2SDiego Novillo cl::opt<ProfileKinds> ProfileKind( 2394d5336ae2SDiego Novillo cl::desc("Profile kind:"), cl::init(instr), 2395d5336ae2SDiego Novillo cl::values(clEnumVal(instr, "Instrumentation profile (default)"), 2396732afdd0SMehdi Amini clEnumVal(sample, "Sample profile"))); 2397801b5319SXinliang David Li cl::opt<uint32_t> TopNFunctions( 2398801b5319SXinliang David Li "topn", cl::init(0), 2399801b5319SXinliang David Li cl::desc("Show the list of functions with the largest internal counts")); 240052aa224aSRong Xu cl::opt<uint32_t> ValueCutoff( 240152aa224aSRong Xu "value-cutoff", cl::init(0), 240252aa224aSRong Xu cl::desc("Set the count value cutoff. Functions with the maximum count " 240352aa224aSRong Xu "less than this value will not be printed out. (Default is 0)")); 240452aa224aSRong Xu cl::opt<bool> OnlyListBelow( 240552aa224aSRong Xu "list-below-cutoff", cl::init(false), 240652aa224aSRong Xu cl::desc("Only output names of functions whose max count values are " 240752aa224aSRong Xu "below the cutoff value")); 2408798e59b8SWei Mi cl::opt<bool> ShowProfileSymbolList( 2409798e59b8SWei Mi "show-prof-sym-list", cl::init(false), 2410798e59b8SWei Mi cl::desc("Show profile symbol list if it exists in the profile. ")); 2411eee532cdSWei Mi cl::opt<bool> ShowSectionInfoOnly( 2412eee532cdSWei Mi "show-sec-info-only", cl::init(false), 2413eee532cdSWei Mi cl::desc("Show the information of each section in the sample profile. " 2414eee532cdSWei Mi "The flag is only usable when the sample profile is in " 2415eee532cdSWei Mi "extbinary format")); 2416798e59b8SWei Mi 2417d5336ae2SDiego Novillo cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); 2418d5336ae2SDiego Novillo 2419d5336ae2SDiego Novillo if (OutputFilename.empty()) 2420d5336ae2SDiego Novillo OutputFilename = "-"; 2421d5336ae2SDiego Novillo 2422f0d3dcecSRong Xu if (!Filename.compare(OutputFilename)) { 2423f0d3dcecSRong Xu errs() << sys::path::filename(argv[0]) 2424f0d3dcecSRong Xu << ": Input file name cannot be the same as the output file name!\n"; 2425f0d3dcecSRong Xu return 1; 2426f0d3dcecSRong Xu } 2427f0d3dcecSRong Xu 2428d5336ae2SDiego Novillo std::error_code EC; 2429d9b948b6SFangrui Song raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_Text); 2430d5336ae2SDiego Novillo if (EC) 24314f823667SNathan Slingerland exitWithErrorCode(EC, OutputFilename); 2432d5336ae2SDiego Novillo 2433d5336ae2SDiego Novillo if (ShowAllFunctions && !ShowFunction.empty()) 2434e46b7565SJonas Devlieghere WithColor::warning() << "-function argument ignored: showing all functions\n"; 2435d5336ae2SDiego Novillo 2436d5336ae2SDiego Novillo if (ProfileKind == instr) 2437801b5319SXinliang David Li return showInstrProfile(Filename, ShowCounts, TopNFunctions, 2438801b5319SXinliang David Li ShowIndirectCallTargets, ShowMemOPSizes, 2439801b5319SXinliang David Li ShowDetailedSummary, DetailedSummaryCutoffs, 2440a6ff69f6SRong Xu ShowAllFunctions, ShowCS, ValueCutoff, 2441a6ff69f6SRong Xu OnlyListBelow, ShowFunction, TextFormat, OS); 2442d5336ae2SDiego Novillo else 2443d5336ae2SDiego Novillo return showSampleProfile(Filename, ShowCounts, ShowAllFunctions, 244417fc6518SWenlei He ShowDetailedSummary, ShowFunction, 244553cf5302Sweihe ShowProfileSymbolList, ShowSectionInfoOnly, 244653cf5302Sweihe ShowHotFuncList, OS); 2447d5336ae2SDiego Novillo } 2448d5336ae2SDiego Novillo 2449618bcea7SJustin Bogner int main(int argc, const char *argv[]) { 2450197194b6SRui Ueyama InitLLVM X(argc, argv); 2451618bcea7SJustin Bogner 2452618bcea7SJustin Bogner StringRef ProgName(sys::path::filename(argv[0])); 2453618bcea7SJustin Bogner if (argc > 1) { 2454e6cb63e4SCraig Topper int (*func)(int, const char *[]) = nullptr; 2455618bcea7SJustin Bogner 2456618bcea7SJustin Bogner if (strcmp(argv[1], "merge") == 0) 2457618bcea7SJustin Bogner func = merge_main; 24589af28ef9SJustin Bogner else if (strcmp(argv[1], "show") == 0) 24599af28ef9SJustin Bogner func = show_main; 2460998b97f6SRong Xu else if (strcmp(argv[1], "overlap") == 0) 2461998b97f6SRong Xu func = overlap_main; 2462618bcea7SJustin Bogner 2463618bcea7SJustin Bogner if (func) { 2464618bcea7SJustin Bogner std::string Invocation(ProgName.str() + " " + argv[1]); 2465618bcea7SJustin Bogner argv[1] = Invocation.c_str(); 2466618bcea7SJustin Bogner return func(argc - 1, argv + 1); 2467618bcea7SJustin Bogner } 2468618bcea7SJustin Bogner 2469d3babdbcSDiego Novillo if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 || 2470618bcea7SJustin Bogner strcmp(argv[1], "--help") == 0) { 2471618bcea7SJustin Bogner 2472618bcea7SJustin Bogner errs() << "OVERVIEW: LLVM profile data tools\n\n" 2473618bcea7SJustin Bogner << "USAGE: " << ProgName << " <command> [args...]\n" 2474618bcea7SJustin Bogner << "USAGE: " << ProgName << " <command> -help\n\n" 2475253eb17bSJustin Bogner << "See each individual command --help for more details.\n" 2476998b97f6SRong Xu << "Available commands: merge, show, overlap\n"; 2477618bcea7SJustin Bogner return 0; 2478618bcea7SJustin Bogner } 2479618bcea7SJustin Bogner } 2480618bcea7SJustin Bogner 2481618bcea7SJustin Bogner if (argc < 2) 2482618bcea7SJustin Bogner errs() << ProgName << ": No command specified!\n"; 2483618bcea7SJustin Bogner else 2484618bcea7SJustin Bogner errs() << ProgName << ": Unknown command!\n"; 2485618bcea7SJustin Bogner 2486998b97f6SRong Xu errs() << "USAGE: " << ProgName << " <merge|show|overlap> [args...]\n"; 2487618bcea7SJustin Bogner return 1; 2488618bcea7SJustin Bogner } 2489