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"
1665d7fd02SEllis Hoag #include "llvm/DebugInfo/DWARF/DWARFContext.h"
17d9903888SChandler Carruth #include "llvm/IR/LLVMContext.h"
1865d7fd02SEllis Hoag #include "llvm/Object/Binary.h"
1965d7fd02SEllis Hoag #include "llvm/ProfileData/InstrProfCorrelator.h"
20f8d79198SJustin Bogner #include "llvm/ProfileData/InstrProfReader.h"
21b9bd7f85SJustin Bogner #include "llvm/ProfileData/InstrProfWriter.h"
22216575e5SSnehasish Kumar #include "llvm/ProfileData/MemProf.h"
23d68aae24SEaswaran Raman #include "llvm/ProfileData/ProfileCommon.h"
247cca33b4SSnehasish Kumar #include "llvm/ProfileData/RawMemProfReader.h"
25d5336ae2SDiego Novillo #include "llvm/ProfileData/SampleProfReader.h"
26d5336ae2SDiego Novillo #include "llvm/ProfileData/SampleProfWriter.h"
27846a627fSDuncan P. N. Exon Smith #include "llvm/Support/CommandLine.h"
288d581857SRong Xu #include "llvm/Support/Discriminator.h"
297f5b47ddSNathan Slingerland #include "llvm/Support/Errc.h"
30d59664f4SBenjamin Kramer #include "llvm/Support/FileSystem.h"
31423380f9SJustin Bogner #include "llvm/Support/Format.h"
3253cf5302Sweihe #include "llvm/Support/FormattedStream.h"
33197194b6SRui Ueyama #include "llvm/Support/InitLLVM.h"
34846a627fSDuncan P. N. Exon Smith #include "llvm/Support/MemoryBuffer.h"
3516132e6fSBenjamin Kramer #include "llvm/Support/Path.h"
365342dd6bSBruno Ricci #include "llvm/Support/ThreadPool.h"
3753cf5302Sweihe #include "llvm/Support/Threading.h"
38ef598759SFangrui Song #include "llvm/Support/WithColor.h"
39846a627fSDuncan P. N. Exon Smith #include "llvm/Support/raw_ostream.h"
407f5b47ddSNathan Slingerland #include <algorithm>
418ef5710eSLuboš Luňák #include <queue>
42846a627fSDuncan P. N. Exon Smith
43846a627fSDuncan P. N. Exon Smith using namespace llvm;
44846a627fSDuncan P. N. Exon Smith
45a0c0857eSWei Mi enum ProfileFormat {
46a0c0857eSWei Mi PF_None = 0,
47a0c0857eSWei Mi PF_Text,
48a0c0857eSWei Mi PF_Compact_Binary,
49be907324SWei Mi PF_Ext_Binary,
50a0c0857eSWei Mi PF_GCC,
51d9be2c7eSWei Mi PF_Binary
52a0c0857eSWei Mi };
536f7c19a4SXinliang David Li
warn(Twine Message,std::string Whence="",std::string Hint="")54e46b7565SJonas Devlieghere static void warn(Twine Message, std::string Whence = "",
55faaa42adSVedant Kumar std::string Hint = "") {
56e46b7565SJonas Devlieghere WithColor::warning();
57f8d79198SJustin Bogner if (!Whence.empty())
58f8d79198SJustin Bogner errs() << Whence << ": ";
59f8d79198SJustin Bogner errs() << Message << "\n";
604f823667SNathan Slingerland if (!Hint.empty())
61e46b7565SJonas Devlieghere WithColor::note() << Hint << "\n";
62188efda5SVedant Kumar }
63188efda5SVedant Kumar
warn(Error E,StringRef Whence="")646da7d314SMatthew Voss static void warn(Error E, StringRef Whence = "") {
656da7d314SMatthew Voss if (E.isA<InstrProfError>()) {
666da7d314SMatthew Voss handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
676da7d314SMatthew Voss warn(IPE.message(), std::string(Whence), std::string(""));
686da7d314SMatthew Voss });
696da7d314SMatthew Voss }
706da7d314SMatthew Voss }
716da7d314SMatthew Voss
exitWithError(Twine Message,std::string Whence="",std::string Hint="")72188efda5SVedant Kumar static void exitWithError(Twine Message, std::string Whence = "",
73188efda5SVedant Kumar std::string Hint = "") {
74e46b7565SJonas Devlieghere WithColor::error();
75e46b7565SJonas Devlieghere if (!Whence.empty())
76e46b7565SJonas Devlieghere errs() << Whence << ": ";
77e46b7565SJonas Devlieghere errs() << Message << "\n";
78e46b7565SJonas Devlieghere if (!Hint.empty())
79e46b7565SJonas Devlieghere WithColor::note() << Hint << "\n";
80846a627fSDuncan P. N. Exon Smith ::exit(1);
81846a627fSDuncan P. N. Exon Smith }
82846a627fSDuncan P. N. Exon Smith
exitWithError(Error E,StringRef Whence="")839152fd17SVedant Kumar static void exitWithError(Error E, StringRef Whence = "") {
849152fd17SVedant Kumar if (E.isA<InstrProfError>()) {
859152fd17SVedant Kumar handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
869152fd17SVedant Kumar instrprof_error instrError = IPE.get();
879152fd17SVedant Kumar StringRef Hint = "";
884f823667SNathan Slingerland if (instrError == instrprof_error::unrecognized_format) {
897cca33b4SSnehasish Kumar // Hint in case user missed specifying the profile type.
907cca33b4SSnehasish Kumar Hint = "Perhaps you forgot to use the --sample or --memory option?";
914f823667SNathan Slingerland }
92adcd0268SBenjamin Kramer exitWithError(IPE.message(), std::string(Whence), std::string(Hint));
939152fd17SVedant Kumar });
9434a62f96SFangrui Song return;
954f823667SNathan Slingerland }
969152fd17SVedant Kumar
97adcd0268SBenjamin Kramer exitWithError(toString(std::move(E)), std::string(Whence));
989152fd17SVedant Kumar }
999152fd17SVedant Kumar
exitWithErrorCode(std::error_code EC,StringRef Whence="")1009152fd17SVedant Kumar static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") {
101adcd0268SBenjamin Kramer exitWithError(EC.message(), std::string(Whence));
1024f823667SNathan Slingerland }
1034f823667SNathan Slingerland
10402b6fa90SDuncan P. N. Exon Smith namespace {
1057cca33b4SSnehasish Kumar enum ProfileKinds { instr, sample, memory };
1060fcfe897SVedant Kumar enum FailureMode { failIfAnyAreInvalid, failIfAllAreInvalid };
1070fcfe897SVedant Kumar }
1080fcfe897SVedant Kumar
warnOrExitGivenError(FailureMode FailMode,std::error_code EC,StringRef Whence="")1090fcfe897SVedant Kumar static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC,
1100fcfe897SVedant Kumar StringRef Whence = "") {
1110fcfe897SVedant Kumar if (FailMode == failIfAnyAreInvalid)
1120fcfe897SVedant Kumar exitWithErrorCode(EC, Whence);
1130fcfe897SVedant Kumar else
114adcd0268SBenjamin Kramer warn(EC.message(), std::string(Whence));
11502b6fa90SDuncan P. N. Exon Smith }
116618bcea7SJustin Bogner
handleMergeWriterError(Error E,StringRef WhenceFile="",StringRef WhenceFunction="",bool ShowHint=true)1179152fd17SVedant Kumar static void handleMergeWriterError(Error E, StringRef WhenceFile = "",
118e6e30d5eSNathan Slingerland StringRef WhenceFunction = "",
119d3babdbcSDiego Novillo bool ShowHint = true) {
120e6e30d5eSNathan Slingerland if (!WhenceFile.empty())
121e6e30d5eSNathan Slingerland errs() << WhenceFile << ": ";
122e6e30d5eSNathan Slingerland if (!WhenceFunction.empty())
123e6e30d5eSNathan Slingerland errs() << WhenceFunction << ": ";
1249152fd17SVedant Kumar
1259152fd17SVedant Kumar auto IPE = instrprof_error::success;
1269152fd17SVedant Kumar E = handleErrors(std::move(E),
1279152fd17SVedant Kumar [&IPE](std::unique_ptr<InstrProfError> E) -> Error {
1289152fd17SVedant Kumar IPE = E->get();
1299152fd17SVedant Kumar return Error(std::move(E));
1309152fd17SVedant Kumar });
1319152fd17SVedant Kumar errs() << toString(std::move(E)) << "\n";
132e6e30d5eSNathan Slingerland
133e6e30d5eSNathan Slingerland if (ShowHint) {
134e6e30d5eSNathan Slingerland StringRef Hint = "";
1359152fd17SVedant Kumar if (IPE != instrprof_error::success) {
1369152fd17SVedant Kumar switch (IPE) {
13711c938d1SNathan Slingerland case instrprof_error::hash_mismatch:
13811c938d1SNathan Slingerland case instrprof_error::count_mismatch:
13911c938d1SNathan Slingerland case instrprof_error::value_site_count_mismatch:
140d3babdbcSDiego Novillo Hint = "Make sure that all profile data to be merged is generated "
141e6e30d5eSNathan Slingerland "from the same binary.";
14211c938d1SNathan Slingerland break;
143b2d95f0dSNathan Slingerland default:
144b2d95f0dSNathan Slingerland break;
145e6e30d5eSNathan Slingerland }
146e6e30d5eSNathan Slingerland }
147e6e30d5eSNathan Slingerland
148e6e30d5eSNathan Slingerland if (!Hint.empty())
149e6e30d5eSNathan Slingerland errs() << Hint << "\n";
150e6e30d5eSNathan Slingerland }
151e6e30d5eSNathan Slingerland }
152e6e30d5eSNathan Slingerland
1533164fcfdSRichard Smith namespace {
1543164fcfdSRichard Smith /// A remapper from original symbol names to new symbol names based on a file
1553164fcfdSRichard Smith /// containing a list of mappings from old name to new name.
1563164fcfdSRichard Smith class SymbolRemapper {
1573164fcfdSRichard Smith std::unique_ptr<MemoryBuffer> File;
1583164fcfdSRichard Smith DenseMap<StringRef, StringRef> RemappingTable;
1593164fcfdSRichard Smith
1603164fcfdSRichard Smith public:
1613164fcfdSRichard Smith /// Build a SymbolRemapper from a file containing a list of old/new symbols.
create(StringRef InputFile)1623164fcfdSRichard Smith static std::unique_ptr<SymbolRemapper> create(StringRef InputFile) {
1633164fcfdSRichard Smith auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);
1643164fcfdSRichard Smith if (!BufOrError)
1653164fcfdSRichard Smith exitWithErrorCode(BufOrError.getError(), InputFile);
1663164fcfdSRichard Smith
1670eaee545SJonas Devlieghere auto Remapper = std::make_unique<SymbolRemapper>();
1683164fcfdSRichard Smith Remapper->File = std::move(BufOrError.get());
1693164fcfdSRichard Smith
1703164fcfdSRichard Smith for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#');
1713164fcfdSRichard Smith !LineIt.is_at_eof(); ++LineIt) {
1723164fcfdSRichard Smith std::pair<StringRef, StringRef> Parts = LineIt->split(' ');
1733164fcfdSRichard Smith if (Parts.first.empty() || Parts.second.empty() ||
1743164fcfdSRichard Smith Parts.second.count(' ')) {
1753164fcfdSRichard Smith exitWithError("unexpected line in remapping file",
1763164fcfdSRichard Smith (InputFile + ":" + Twine(LineIt.line_number())).str(),
1773164fcfdSRichard Smith "expected 'old_symbol new_symbol'");
1783164fcfdSRichard Smith }
1793164fcfdSRichard Smith Remapper->RemappingTable.insert(Parts);
1803164fcfdSRichard Smith }
1813164fcfdSRichard Smith return Remapper;
1823164fcfdSRichard Smith }
1833164fcfdSRichard Smith
1843164fcfdSRichard Smith /// Attempt to map the given old symbol into a new symbol.
1853164fcfdSRichard Smith ///
1863164fcfdSRichard Smith /// \return The new symbol, or \p Name if no such symbol was found.
operator ()(StringRef Name)1873164fcfdSRichard Smith StringRef operator()(StringRef Name) {
1883164fcfdSRichard Smith StringRef New = RemappingTable.lookup(Name);
1893164fcfdSRichard Smith return New.empty() ? Name : New;
1903164fcfdSRichard Smith }
1913164fcfdSRichard Smith };
1923164fcfdSRichard Smith }
1933164fcfdSRichard Smith
1947f5b47ddSNathan Slingerland struct WeightedFile {
1959a1bfcfaSXinliang David Li std::string Filename;
1967f5b47ddSNathan Slingerland uint64_t Weight;
1977f5b47ddSNathan Slingerland };
1987f5b47ddSNathan Slingerland typedef SmallVector<WeightedFile, 5> WeightedFileVector;
1997f5b47ddSNathan Slingerland
200e3a0bf50SVedant Kumar /// Keep track of merged data and reported errors.
201e3a0bf50SVedant Kumar struct WriterContext {
202e3a0bf50SVedant Kumar std::mutex Lock;
203e3a0bf50SVedant Kumar InstrProfWriter Writer;
2040fcfe897SVedant Kumar std::vector<std::pair<Error, std::string>> Errors;
205e3a0bf50SVedant Kumar std::mutex &ErrLock;
206e3a0bf50SVedant Kumar SmallSet<instrprof_error, 4> &WriterErrorCodes;
207e3a0bf50SVedant Kumar
WriterContextWriterContext208e3a0bf50SVedant Kumar WriterContext(bool IsSparse, std::mutex &ErrLock,
209e3a0bf50SVedant Kumar SmallSet<instrprof_error, 4> &WriterErrorCodes)
210f44473ecSKazu Hirata : Writer(IsSparse), ErrLock(ErrLock), WriterErrorCodes(WriterErrorCodes) {
211f44473ecSKazu Hirata }
212e3a0bf50SVedant Kumar };
213e3a0bf50SVedant Kumar
214998b97f6SRong Xu /// Computer the overlap b/w profile BaseFilename and TestFileName,
215998b97f6SRong Xu /// and store the program level result to Overlap.
overlapInput(const std::string & BaseFilename,const std::string & TestFilename,WriterContext * WC,OverlapStats & Overlap,const OverlapFuncFilters & FuncFilter,raw_fd_ostream & OS,bool IsCS)216998b97f6SRong Xu static void overlapInput(const std::string &BaseFilename,
217998b97f6SRong Xu const std::string &TestFilename, WriterContext *WC,
218998b97f6SRong Xu OverlapStats &Overlap,
219998b97f6SRong Xu const OverlapFuncFilters &FuncFilter,
220998b97f6SRong Xu raw_fd_ostream &OS, bool IsCS) {
221998b97f6SRong Xu auto ReaderOrErr = InstrProfReader::create(TestFilename);
222998b97f6SRong Xu if (Error E = ReaderOrErr.takeError()) {
223998b97f6SRong Xu // Skip the empty profiles by returning sliently.
224998b97f6SRong Xu instrprof_error IPE = InstrProfError::take(std::move(E));
225998b97f6SRong Xu if (IPE != instrprof_error::empty_raw_profile)
2260fcfe897SVedant Kumar WC->Errors.emplace_back(make_error<InstrProfError>(IPE), TestFilename);
227998b97f6SRong Xu return;
228998b97f6SRong Xu }
229998b97f6SRong Xu
230998b97f6SRong Xu auto Reader = std::move(ReaderOrErr.get());
231998b97f6SRong Xu for (auto &I : *Reader) {
232998b97f6SRong Xu OverlapStats FuncOverlap(OverlapStats::FunctionLevel);
233998b97f6SRong Xu FuncOverlap.setFuncInfo(I.Name, I.Hash);
234998b97f6SRong Xu
235998b97f6SRong Xu WC->Writer.overlapRecord(std::move(I), Overlap, FuncOverlap, FuncFilter);
236998b97f6SRong Xu FuncOverlap.dump(OS);
237998b97f6SRong Xu }
238998b97f6SRong Xu }
239998b97f6SRong Xu
240e3a0bf50SVedant Kumar /// Load an input into a writer context.
loadInput(const WeightedFile & Input,SymbolRemapper * Remapper,const InstrProfCorrelator * Correlator,const StringRef ProfiledBinary,WriterContext * WC)2413164fcfdSRichard Smith static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper,
24265d7fd02SEllis Hoag const InstrProfCorrelator *Correlator,
2430a418490SSnehasish Kumar const StringRef ProfiledBinary, WriterContext *WC) {
244e3a0bf50SVedant Kumar std::unique_lock<std::mutex> CtxGuard{WC->Lock};
245e3a0bf50SVedant Kumar
246faaa42adSVedant Kumar // Copy the filename, because llvm::ThreadPool copied the input "const
247faaa42adSVedant Kumar // WeightedFile &" by value, making a reference to the filename within it
248faaa42adSVedant Kumar // invalid outside of this packaged task.
2490fcfe897SVedant Kumar std::string Filename = Input.Filename;
250e3a0bf50SVedant Kumar
2510a418490SSnehasish Kumar using ::llvm::memprof::RawMemProfReader;
2520a418490SSnehasish Kumar if (RawMemProfReader::hasFormat(Input.Filename)) {
2530a418490SSnehasish Kumar auto ReaderOrErr = RawMemProfReader::create(Input.Filename, ProfiledBinary);
2540a418490SSnehasish Kumar if (!ReaderOrErr) {
2550a418490SSnehasish Kumar exitWithError(ReaderOrErr.takeError(), Input.Filename);
2560a418490SSnehasish Kumar }
2570a418490SSnehasish Kumar std::unique_ptr<RawMemProfReader> Reader = std::move(ReaderOrErr.get());
2580a418490SSnehasish Kumar // Check if the profile types can be merged, e.g. clang frontend profiles
2590a418490SSnehasish Kumar // should not be merged with memprof profiles.
2600a418490SSnehasish Kumar if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) {
2610a418490SSnehasish Kumar consumeError(std::move(E));
2620a418490SSnehasish Kumar WC->Errors.emplace_back(
2630a418490SSnehasish Kumar make_error<StringError>(
2640a418490SSnehasish Kumar "Cannot merge MemProf profile with Clang generated profile.",
2650a418490SSnehasish Kumar std::error_code()),
2660a418490SSnehasish Kumar Filename);
2670a418490SSnehasish Kumar return;
2680a418490SSnehasish Kumar }
2690a418490SSnehasish Kumar
2706dd6a616SSnehasish Kumar auto MemProfError = [&](Error E) {
2710a418490SSnehasish Kumar instrprof_error IPE = InstrProfError::take(std::move(E));
2720a418490SSnehasish Kumar WC->Errors.emplace_back(make_error<InstrProfError>(IPE), Filename);
2736dd6a616SSnehasish Kumar };
2746dd6a616SSnehasish Kumar
2756dd6a616SSnehasish Kumar // Add the frame mappings into the writer context.
2766dd6a616SSnehasish Kumar const auto &IdToFrame = Reader->getFrameMapping();
2776dd6a616SSnehasish Kumar for (const auto &I : IdToFrame) {
2786dd6a616SSnehasish Kumar bool Succeeded = WC->Writer.addMemProfFrame(
2796dd6a616SSnehasish Kumar /*Id=*/I.first, /*Frame=*/I.getSecond(), MemProfError);
2806dd6a616SSnehasish Kumar // If we weren't able to add the frame mappings then it doesn't make sense
2816dd6a616SSnehasish Kumar // to try to add the records from this profile.
2826dd6a616SSnehasish Kumar if (!Succeeded)
2836dd6a616SSnehasish Kumar return;
2846dd6a616SSnehasish Kumar }
2856dd6a616SSnehasish Kumar const auto &FunctionProfileData = Reader->getProfileData();
2866dd6a616SSnehasish Kumar // Add the memprof records into the writer context.
2876dd6a616SSnehasish Kumar for (const auto &I : FunctionProfileData) {
2886dd6a616SSnehasish Kumar WC->Writer.addMemProfRecord(/*Id=*/I.first, /*Record=*/I.second);
2890a418490SSnehasish Kumar }
2900a418490SSnehasish Kumar return;
2910a418490SSnehasish Kumar }
2920a418490SSnehasish Kumar
29365d7fd02SEllis Hoag auto ReaderOrErr = InstrProfReader::create(Input.Filename, Correlator);
2942c684cfdSRong Xu if (Error E = ReaderOrErr.takeError()) {
2952c684cfdSRong Xu // Skip the empty profiles by returning sliently.
2962c684cfdSRong Xu instrprof_error IPE = InstrProfError::take(std::move(E));
2972c684cfdSRong Xu if (IPE != instrprof_error::empty_raw_profile)
2980fcfe897SVedant Kumar WC->Errors.emplace_back(make_error<InstrProfError>(IPE), Filename);
299e3a0bf50SVedant Kumar return;
3002c684cfdSRong Xu }
301e3a0bf50SVedant Kumar
302e3a0bf50SVedant Kumar auto Reader = std::move(ReaderOrErr.get());
30313d89477SSnehasish Kumar if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) {
304142d522dSMarkus Böck consumeError(std::move(E));
3050fcfe897SVedant Kumar WC->Errors.emplace_back(
3060fcfe897SVedant Kumar make_error<StringError>(
307e3a0bf50SVedant Kumar "Merge IR generated profile with Clang generated profile.",
3080fcfe897SVedant Kumar std::error_code()),
3090fcfe897SVedant Kumar Filename);
310e3a0bf50SVedant Kumar return;
311e3a0bf50SVedant Kumar }
312e3a0bf50SVedant Kumar
313e3a0bf50SVedant Kumar for (auto &I : *Reader) {
3143164fcfdSRichard Smith if (Remapper)
3153164fcfdSRichard Smith I.Name = (*Remapper)(I.Name);
316fe90d86cSRong Xu const StringRef FuncName = I.Name;
31798cce003SDavid Blaikie bool Reported = false;
31898cce003SDavid Blaikie WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) {
31998cce003SDavid Blaikie if (Reported) {
32098cce003SDavid Blaikie consumeError(std::move(E));
32198cce003SDavid Blaikie return;
32298cce003SDavid Blaikie }
32398cce003SDavid Blaikie Reported = true;
324e3a0bf50SVedant Kumar // Only show hint the first time an error occurs.
325e3a0bf50SVedant Kumar instrprof_error IPE = InstrProfError::take(std::move(E));
326e3a0bf50SVedant Kumar std::unique_lock<std::mutex> ErrGuard{WC->ErrLock};
327e3a0bf50SVedant Kumar bool firstTime = WC->WriterErrorCodes.insert(IPE).second;
328e3a0bf50SVedant Kumar handleMergeWriterError(make_error<InstrProfError>(IPE), Input.Filename,
329fe90d86cSRong Xu FuncName, firstTime);
33098cce003SDavid Blaikie });
331e3a0bf50SVedant Kumar }
3320fcfe897SVedant Kumar if (Reader->hasError())
3330fcfe897SVedant Kumar if (Error E = Reader->getError())
3340fcfe897SVedant Kumar WC->Errors.emplace_back(std::move(E), Filename);
335e3a0bf50SVedant Kumar }
336e3a0bf50SVedant Kumar
337e3a0bf50SVedant Kumar /// Merge the \p Src writer context into \p Dst.
mergeWriterContexts(WriterContext * Dst,WriterContext * Src)338e3a0bf50SVedant Kumar static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) {
3390fcfe897SVedant Kumar for (auto &ErrorPair : Src->Errors)
3400fcfe897SVedant Kumar Dst->Errors.push_back(std::move(ErrorPair));
3410fcfe897SVedant Kumar Src->Errors.clear();
342faaa42adSVedant Kumar
34398cce003SDavid Blaikie Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) {
3440fcfe897SVedant Kumar instrprof_error IPE = InstrProfError::take(std::move(E));
3450fcfe897SVedant Kumar std::unique_lock<std::mutex> ErrGuard{Dst->ErrLock};
3460fcfe897SVedant Kumar bool firstTime = Dst->WriterErrorCodes.insert(IPE).second;
3470fcfe897SVedant Kumar if (firstTime)
3480fcfe897SVedant Kumar warn(toString(make_error<InstrProfError>(IPE)));
34998cce003SDavid Blaikie });
350e3a0bf50SVedant Kumar }
351e3a0bf50SVedant Kumar
writeInstrProfile(StringRef OutputFilename,ProfileFormat OutputFormat,InstrProfWriter & Writer)35278fe6a3eSWei Mi static void writeInstrProfile(StringRef OutputFilename,
35378fe6a3eSWei Mi ProfileFormat OutputFormat,
35478fe6a3eSWei Mi InstrProfWriter &Writer) {
35578fe6a3eSWei Mi std::error_code EC;
3568ad998a6SAbhina Sreeskantharajan raw_fd_ostream Output(OutputFilename.data(), EC,
35782b3e28eSAbhina Sreeskantharajan OutputFormat == PF_Text ? sys::fs::OF_TextWithCRLF
3588ad998a6SAbhina Sreeskantharajan : sys::fs::OF_None);
35978fe6a3eSWei Mi if (EC)
36078fe6a3eSWei Mi exitWithErrorCode(EC, OutputFilename);
36178fe6a3eSWei Mi
36278fe6a3eSWei Mi if (OutputFormat == PF_Text) {
36378fe6a3eSWei Mi if (Error E = Writer.writeText(Output))
3646da7d314SMatthew Voss warn(std::move(E));
36578fe6a3eSWei Mi } else {
366ea23c38dSFangrui Song if (Output.is_displayed())
367ea23c38dSFangrui Song exitWithError("cannot write a non-text format profile to the terminal");
3686da7d314SMatthew Voss if (Error E = Writer.write(Output))
3696da7d314SMatthew Voss warn(std::move(E));
37078fe6a3eSWei Mi }
37178fe6a3eSWei Mi }
37278fe6a3eSWei Mi
mergeInstrProfile(const WeightedFileVector & Inputs,StringRef DebugInfoFilename,SymbolRemapper * Remapper,StringRef OutputFilename,ProfileFormat OutputFormat,bool OutputSparse,unsigned NumThreads,FailureMode FailMode,const StringRef ProfiledBinary)3737f5b47ddSNathan Slingerland static void mergeInstrProfile(const WeightedFileVector &Inputs,
37465d7fd02SEllis Hoag StringRef DebugInfoFilename,
3753164fcfdSRichard Smith SymbolRemapper *Remapper,
3766f7c19a4SXinliang David Li StringRef OutputFilename,
377e3a0bf50SVedant Kumar ProfileFormat OutputFormat, bool OutputSparse,
3780a418490SSnehasish Kumar unsigned NumThreads, FailureMode FailMode,
3790a418490SSnehasish Kumar const StringRef ProfiledBinary) {
380d9be2c7eSWei Mi if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary &&
381be907324SWei Mi OutputFormat != PF_Ext_Binary && OutputFormat != PF_Text)
3828ea2a58aSFangrui Song exitWithError("unknown format is specified");
3836f7c19a4SXinliang David Li
38465d7fd02SEllis Hoag std::unique_ptr<InstrProfCorrelator> Correlator;
38565d7fd02SEllis Hoag if (!DebugInfoFilename.empty()) {
38665d7fd02SEllis Hoag if (auto Err =
38765d7fd02SEllis Hoag InstrProfCorrelator::get(DebugInfoFilename).moveInto(Correlator))
38865d7fd02SEllis Hoag exitWithError(std::move(Err), DebugInfoFilename);
38965d7fd02SEllis Hoag if (auto Err = Correlator->correlateProfileData())
39065d7fd02SEllis Hoag exitWithError(std::move(Err), DebugInfoFilename);
39165d7fd02SEllis Hoag }
39265d7fd02SEllis Hoag
393e3a0bf50SVedant Kumar std::mutex ErrorLock;
3949152fd17SVedant Kumar SmallSet<instrprof_error, 4> WriterErrorCodes;
395e3a0bf50SVedant Kumar
396e3a0bf50SVedant Kumar // If NumThreads is not specified, auto-detect a good default.
397e3a0bf50SVedant Kumar if (NumThreads == 0)
3988404aeb5SAlexandre Ganea NumThreads = std::min(hardware_concurrency().compute_thread_count(),
3998404aeb5SAlexandre Ganea unsigned((Inputs.size() + 1) / 2));
4008404aeb5SAlexandre Ganea // FIXME: There's a bug here, where setting NumThreads = Inputs.size() fails
4018404aeb5SAlexandre Ganea // the merge_empty_profile.test because the InstrProfWriter.ProfileKind isn't
4028404aeb5SAlexandre Ganea // merged, thus the emitted file ends up with a PF_Unknown kind.
403e3a0bf50SVedant Kumar
404e3a0bf50SVedant Kumar // Initialize the writer contexts.
405e3a0bf50SVedant Kumar SmallVector<std::unique_ptr<WriterContext>, 4> Contexts;
406e3a0bf50SVedant Kumar for (unsigned I = 0; I < NumThreads; ++I)
4070eaee545SJonas Devlieghere Contexts.emplace_back(std::make_unique<WriterContext>(
408e3a0bf50SVedant Kumar OutputSparse, ErrorLock, WriterErrorCodes));
409e3a0bf50SVedant Kumar
410e3a0bf50SVedant Kumar if (NumThreads == 1) {
411e3a0bf50SVedant Kumar for (const auto &Input : Inputs)
4120a418490SSnehasish Kumar loadInput(Input, Remapper, Correlator.get(), ProfiledBinary,
4130a418490SSnehasish Kumar Contexts[0].get());
414e3a0bf50SVedant Kumar } else {
4158404aeb5SAlexandre Ganea ThreadPool Pool(hardware_concurrency(NumThreads));
416e3a0bf50SVedant Kumar
417e3a0bf50SVedant Kumar // Load the inputs in parallel (N/NumThreads serial steps).
418e3a0bf50SVedant Kumar unsigned Ctx = 0;
4197f5b47ddSNathan Slingerland for (const auto &Input : Inputs) {
4200a418490SSnehasish Kumar Pool.async(loadInput, Input, Remapper, Correlator.get(), ProfiledBinary,
42165d7fd02SEllis Hoag Contexts[Ctx].get());
422e3a0bf50SVedant Kumar Ctx = (Ctx + 1) % NumThreads;
423e3a0bf50SVedant Kumar }
424e3a0bf50SVedant Kumar Pool.wait();
42521ab20e0SVedant Kumar
426e3a0bf50SVedant Kumar // Merge the writer contexts together (~ lg(NumThreads) serial steps).
427e3a0bf50SVedant Kumar unsigned Mid = Contexts.size() / 2;
428e3a0bf50SVedant Kumar unsigned End = Contexts.size();
429e3a0bf50SVedant Kumar assert(Mid > 0 && "Expected more than one context");
430e3a0bf50SVedant Kumar do {
431e3a0bf50SVedant Kumar for (unsigned I = 0; I < Mid; ++I)
432e3a0bf50SVedant Kumar Pool.async(mergeWriterContexts, Contexts[I].get(),
433e3a0bf50SVedant Kumar Contexts[I + Mid].get());
434e3a0bf50SVedant Kumar Pool.wait();
435e3a0bf50SVedant Kumar if (End & 1) {
436e3a0bf50SVedant Kumar Pool.async(mergeWriterContexts, Contexts[0].get(),
437e3a0bf50SVedant Kumar Contexts[End - 1].get());
438e3a0bf50SVedant Kumar Pool.wait();
439e3a0bf50SVedant Kumar }
440e3a0bf50SVedant Kumar End = Mid;
441e3a0bf50SVedant Kumar Mid /= 2;
442e3a0bf50SVedant Kumar } while (Mid > 0);
443e3a0bf50SVedant Kumar }
44421ab20e0SVedant Kumar
4450fcfe897SVedant Kumar // Handle deferred errors encountered during merging. If the number of errors
4460fcfe897SVedant Kumar // is equal to the number of inputs the merge failed.
4470fcfe897SVedant Kumar unsigned NumErrors = 0;
448188efda5SVedant Kumar for (std::unique_ptr<WriterContext> &WC : Contexts) {
4490fcfe897SVedant Kumar for (auto &ErrorPair : WC->Errors) {
4500fcfe897SVedant Kumar ++NumErrors;
4510fcfe897SVedant Kumar warn(toString(std::move(ErrorPair.first)), ErrorPair.second);
452188efda5SVedant Kumar }
4530fcfe897SVedant Kumar }
4540fcfe897SVedant Kumar if (NumErrors == Inputs.size() ||
4550fcfe897SVedant Kumar (NumErrors > 0 && FailMode == failIfAnyAreInvalid))
4568ea2a58aSFangrui Song exitWithError("no profile can be merged");
457188efda5SVedant Kumar
45878fe6a3eSWei Mi writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer);
459b5794ca9SVedant Kumar }
460d5336ae2SDiego Novillo
461a23f6234SWei Mi /// The profile entry for a function in instrumentation profile.
462a23f6234SWei Mi struct InstrProfileEntry {
463a23f6234SWei Mi uint64_t MaxCount = 0;
464a23f6234SWei Mi float ZeroCounterRatio = 0.0;
465a23f6234SWei Mi InstrProfRecord *ProfRecord;
466a23f6234SWei Mi InstrProfileEntry(InstrProfRecord *Record);
467a23f6234SWei Mi InstrProfileEntry() = default;
468a23f6234SWei Mi };
469a23f6234SWei Mi
InstrProfileEntry(InstrProfRecord * Record)470a23f6234SWei Mi InstrProfileEntry::InstrProfileEntry(InstrProfRecord *Record) {
471a23f6234SWei Mi ProfRecord = Record;
472a23f6234SWei Mi uint64_t CntNum = Record->Counts.size();
473a23f6234SWei Mi uint64_t ZeroCntNum = 0;
474a23f6234SWei Mi for (size_t I = 0; I < CntNum; ++I) {
475a23f6234SWei Mi MaxCount = std::max(MaxCount, Record->Counts[I]);
476a23f6234SWei Mi ZeroCntNum += !Record->Counts[I];
477a23f6234SWei Mi }
478a23f6234SWei Mi ZeroCounterRatio = (float)ZeroCntNum / CntNum;
479a23f6234SWei Mi }
480a23f6234SWei Mi
481a23f6234SWei Mi /// Either set all the counters in the instr profile entry \p IFE to -1
482a23f6234SWei Mi /// in order to drop the profile or scale up the counters in \p IFP to
483a23f6234SWei Mi /// be above hot threshold. We use the ratio of zero counters in the
484a23f6234SWei Mi /// profile of a function to decide the profile is helpful or harmful
485a23f6234SWei Mi /// for performance, and to choose whether to scale up or drop it.
updateInstrProfileEntry(InstrProfileEntry & IFE,uint64_t HotInstrThreshold,float ZeroCounterThreshold)486a23f6234SWei Mi static void updateInstrProfileEntry(InstrProfileEntry &IFE,
487a23f6234SWei Mi uint64_t HotInstrThreshold,
488a23f6234SWei Mi float ZeroCounterThreshold) {
489a23f6234SWei Mi InstrProfRecord *ProfRecord = IFE.ProfRecord;
490a23f6234SWei Mi if (!IFE.MaxCount || IFE.ZeroCounterRatio > ZeroCounterThreshold) {
491a23f6234SWei Mi // If all or most of the counters of the function are zero, the
492a23f6234SWei Mi // profile is unaccountable and shuld be dropped. Reset all the
493a23f6234SWei Mi // counters to be -1 and PGO profile-use will drop the profile.
494a23f6234SWei Mi // All counters being -1 also implies that the function is hot so
495a23f6234SWei Mi // PGO profile-use will also set the entry count metadata to be
496a23f6234SWei Mi // above hot threshold.
497a23f6234SWei Mi for (size_t I = 0; I < ProfRecord->Counts.size(); ++I)
498a23f6234SWei Mi ProfRecord->Counts[I] = -1;
499a23f6234SWei Mi return;
500a23f6234SWei Mi }
501a23f6234SWei Mi
502a23f6234SWei Mi // Scale up the MaxCount to be multiple times above hot threshold.
503a23f6234SWei Mi const unsigned MultiplyFactor = 3;
504a23f6234SWei Mi uint64_t Numerator = HotInstrThreshold * MultiplyFactor;
505a23f6234SWei Mi uint64_t Denominator = IFE.MaxCount;
506a23f6234SWei Mi ProfRecord->scale(Numerator, Denominator, [&](instrprof_error E) {
507a23f6234SWei Mi warn(toString(make_error<InstrProfError>(E)));
508a23f6234SWei Mi });
509a23f6234SWei Mi }
510a23f6234SWei Mi
511a23f6234SWei Mi const uint64_t ColdPercentileIdx = 15;
512a23f6234SWei Mi const uint64_t HotPercentileIdx = 11;
513a23f6234SWei Mi
5148d581857SRong Xu using sampleprof::FSDiscriminatorPass;
5158d581857SRong Xu
5168d581857SRong Xu // Internal options to set FSDiscriminatorPass. Used in merge and show
5178d581857SRong Xu // commands.
5188d581857SRong Xu static cl::opt<FSDiscriminatorPass> FSDiscriminatorPassOption(
5198d581857SRong Xu "fs-discriminator-pass", cl::init(PassLast), cl::Hidden,
5208d581857SRong Xu cl::desc("Zero out the discriminator bits for the FS discrimiantor "
5218d581857SRong Xu "pass beyond this value. The enum values are defined in "
5228d581857SRong Xu "Support/Discriminator.h"),
5238d581857SRong Xu cl::values(clEnumVal(Base, "Use base discriminators only"),
5248d581857SRong Xu clEnumVal(Pass1, "Use base and pass 1 discriminators"),
5258d581857SRong Xu clEnumVal(Pass2, "Use base and pass 1-2 discriminators"),
5268d581857SRong Xu clEnumVal(Pass3, "Use base and pass 1-3 discriminators"),
5278d581857SRong Xu clEnumVal(PassLast, "Use all discriminator bits (default)")));
5288d581857SRong Xu
getDiscriminatorMask()5298d581857SRong Xu static unsigned getDiscriminatorMask() {
5308d581857SRong Xu return getN1Bits(getFSPassBitEnd(FSDiscriminatorPassOption.getValue()));
5318d581857SRong Xu }
5328d581857SRong Xu
533a23f6234SWei Mi /// Adjust the instr profile in \p WC based on the sample profile in
534a23f6234SWei Mi /// \p Reader.
535a23f6234SWei Mi static void
adjustInstrProfile(std::unique_ptr<WriterContext> & WC,std::unique_ptr<sampleprof::SampleProfileReader> & Reader,unsigned SupplMinSizeThreshold,float ZeroCounterThreshold,unsigned InstrProfColdThreshold)536a23f6234SWei Mi adjustInstrProfile(std::unique_ptr<WriterContext> &WC,
537a23f6234SWei Mi std::unique_ptr<sampleprof::SampleProfileReader> &Reader,
538a23f6234SWei Mi unsigned SupplMinSizeThreshold, float ZeroCounterThreshold,
539a23f6234SWei Mi unsigned InstrProfColdThreshold) {
540a23f6234SWei Mi // Function to its entry in instr profile.
541a23f6234SWei Mi StringMap<InstrProfileEntry> InstrProfileMap;
542a23f6234SWei Mi InstrProfSummaryBuilder IPBuilder(ProfileSummaryBuilder::DefaultCutoffs);
543a23f6234SWei Mi for (auto &PD : WC->Writer.getProfileData()) {
544a23f6234SWei Mi // Populate IPBuilder.
545a23f6234SWei Mi for (const auto &PDV : PD.getValue()) {
546a23f6234SWei Mi InstrProfRecord Record = PDV.second;
547a23f6234SWei Mi IPBuilder.addRecord(Record);
548a23f6234SWei Mi }
549a23f6234SWei Mi
550a23f6234SWei Mi // If a function has multiple entries in instr profile, skip it.
551a23f6234SWei Mi if (PD.getValue().size() != 1)
552a23f6234SWei Mi continue;
553a23f6234SWei Mi
554a23f6234SWei Mi // Initialize InstrProfileMap.
555a23f6234SWei Mi InstrProfRecord *R = &PD.getValue().begin()->second;
556a23f6234SWei Mi InstrProfileMap[PD.getKey()] = InstrProfileEntry(R);
557a23f6234SWei Mi }
558a23f6234SWei Mi
559a23f6234SWei Mi ProfileSummary InstrPS = *IPBuilder.getSummary();
560a23f6234SWei Mi ProfileSummary SamplePS = Reader->getSummary();
561a23f6234SWei Mi
562a23f6234SWei Mi // Compute cold thresholds for instr profile and sample profile.
563a23f6234SWei Mi uint64_t ColdSampleThreshold =
564a23f6234SWei Mi ProfileSummaryBuilder::getEntryForPercentile(
565a23f6234SWei Mi SamplePS.getDetailedSummary(),
566a23f6234SWei Mi ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx])
567a23f6234SWei Mi .MinCount;
568a23f6234SWei Mi uint64_t HotInstrThreshold =
569a23f6234SWei Mi ProfileSummaryBuilder::getEntryForPercentile(
570a23f6234SWei Mi InstrPS.getDetailedSummary(),
571a23f6234SWei Mi ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx])
572a23f6234SWei Mi .MinCount;
573a23f6234SWei Mi uint64_t ColdInstrThreshold =
574a23f6234SWei Mi InstrProfColdThreshold
575a23f6234SWei Mi ? InstrProfColdThreshold
576a23f6234SWei Mi : ProfileSummaryBuilder::getEntryForPercentile(
577a23f6234SWei Mi InstrPS.getDetailedSummary(),
578a23f6234SWei Mi ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx])
579a23f6234SWei Mi .MinCount;
580a23f6234SWei Mi
581a23f6234SWei Mi // Find hot/warm functions in sample profile which is cold in instr profile
582a23f6234SWei Mi // and adjust the profiles of those functions in the instr profile.
583a23f6234SWei Mi for (const auto &PD : Reader->getProfiles()) {
584b9db7036SHongtao Yu auto &FContext = PD.first;
585b9db7036SHongtao Yu const sampleprof::FunctionSamples &FS = PD.second;
586b9db7036SHongtao Yu auto It = InstrProfileMap.find(FContext.toString());
587a23f6234SWei Mi if (FS.getHeadSamples() > ColdSampleThreshold &&
588a23f6234SWei Mi It != InstrProfileMap.end() &&
589a23f6234SWei Mi It->second.MaxCount <= ColdInstrThreshold &&
590a23f6234SWei Mi FS.getBodySamples().size() >= SupplMinSizeThreshold) {
591a23f6234SWei Mi updateInstrProfileEntry(It->second, HotInstrThreshold,
592a23f6234SWei Mi ZeroCounterThreshold);
593a23f6234SWei Mi }
594a23f6234SWei Mi }
595a23f6234SWei Mi }
596a23f6234SWei Mi
597a23f6234SWei Mi /// The main function to supplement instr profile with sample profile.
598a23f6234SWei Mi /// \Inputs contains the instr profile. \p SampleFilename specifies the
599a23f6234SWei Mi /// sample profile. \p OutputFilename specifies the output profile name.
600a23f6234SWei Mi /// \p OutputFormat specifies the output profile format. \p OutputSparse
601a23f6234SWei Mi /// specifies whether to generate sparse profile. \p SupplMinSizeThreshold
602a23f6234SWei Mi /// specifies the minimal size for the functions whose profile will be
603a23f6234SWei Mi /// adjusted. \p ZeroCounterThreshold is the threshold to check whether
604a23f6234SWei Mi /// a function contains too many zero counters and whether its profile
605a23f6234SWei Mi /// should be dropped. \p InstrProfColdThreshold is the user specified
606a23f6234SWei Mi /// cold threshold which will override the cold threshold got from the
607a23f6234SWei Mi /// instr profile summary.
supplementInstrProfile(const WeightedFileVector & Inputs,StringRef SampleFilename,StringRef OutputFilename,ProfileFormat OutputFormat,bool OutputSparse,unsigned SupplMinSizeThreshold,float ZeroCounterThreshold,unsigned InstrProfColdThreshold)608a23f6234SWei Mi static void supplementInstrProfile(
609a23f6234SWei Mi const WeightedFileVector &Inputs, StringRef SampleFilename,
610a23f6234SWei Mi StringRef OutputFilename, ProfileFormat OutputFormat, bool OutputSparse,
611a23f6234SWei Mi unsigned SupplMinSizeThreshold, float ZeroCounterThreshold,
612a23f6234SWei Mi unsigned InstrProfColdThreshold) {
613a23f6234SWei Mi if (OutputFilename.compare("-") == 0)
6148ea2a58aSFangrui Song exitWithError("cannot write indexed profdata format to stdout");
615a23f6234SWei Mi if (Inputs.size() != 1)
6168ea2a58aSFangrui Song exitWithError("expect one input to be an instr profile");
617a23f6234SWei Mi if (Inputs[0].Weight != 1)
6188ea2a58aSFangrui Song exitWithError("expect instr profile doesn't have weight");
619a23f6234SWei Mi
620a23f6234SWei Mi StringRef InstrFilename = Inputs[0].Filename;
621a23f6234SWei Mi
622a23f6234SWei Mi // Read sample profile.
623a23f6234SWei Mi LLVMContext Context;
6248d581857SRong Xu auto ReaderOrErr = sampleprof::SampleProfileReader::create(
6258d581857SRong Xu SampleFilename.str(), Context, FSDiscriminatorPassOption);
626a23f6234SWei Mi if (std::error_code EC = ReaderOrErr.getError())
627a23f6234SWei Mi exitWithErrorCode(EC, SampleFilename);
628a23f6234SWei Mi auto Reader = std::move(ReaderOrErr.get());
629a23f6234SWei Mi if (std::error_code EC = Reader->read())
630a23f6234SWei Mi exitWithErrorCode(EC, SampleFilename);
631a23f6234SWei Mi
632a23f6234SWei Mi // Read instr profile.
633a23f6234SWei Mi std::mutex ErrorLock;
634a23f6234SWei Mi SmallSet<instrprof_error, 4> WriterErrorCodes;
635a23f6234SWei Mi auto WC = std::make_unique<WriterContext>(OutputSparse, ErrorLock,
636a23f6234SWei Mi WriterErrorCodes);
6370a418490SSnehasish Kumar loadInput(Inputs[0], nullptr, nullptr, /*ProfiledBinary=*/"", WC.get());
638a23f6234SWei Mi if (WC->Errors.size() > 0)
639a23f6234SWei Mi exitWithError(std::move(WC->Errors[0].first), InstrFilename);
640a23f6234SWei Mi
641a23f6234SWei Mi adjustInstrProfile(WC, Reader, SupplMinSizeThreshold, ZeroCounterThreshold,
642a23f6234SWei Mi InstrProfColdThreshold);
643a23f6234SWei Mi writeInstrProfile(OutputFilename, OutputFormat, WC->Writer);
644a23f6234SWei Mi }
645a23f6234SWei Mi
6463164fcfdSRichard Smith /// Make a copy of the given function samples with all symbol names remapped
6473164fcfdSRichard Smith /// by the provided symbol remapper.
6483164fcfdSRichard Smith static sampleprof::FunctionSamples
remapSamples(const sampleprof::FunctionSamples & Samples,SymbolRemapper & Remapper,sampleprof_error & Error)6493164fcfdSRichard Smith remapSamples(const sampleprof::FunctionSamples &Samples,
6503164fcfdSRichard Smith SymbolRemapper &Remapper, sampleprof_error &Error) {
6513164fcfdSRichard Smith sampleprof::FunctionSamples Result;
6523164fcfdSRichard Smith Result.setName(Remapper(Samples.getName()));
6533164fcfdSRichard Smith Result.addTotalSamples(Samples.getTotalSamples());
6543164fcfdSRichard Smith Result.addHeadSamples(Samples.getHeadSamples());
6553164fcfdSRichard Smith for (const auto &BodySample : Samples.getBodySamples()) {
6568d581857SRong Xu uint32_t MaskedDiscriminator =
6578d581857SRong Xu BodySample.first.Discriminator & getDiscriminatorMask();
6588d581857SRong Xu Result.addBodySamples(BodySample.first.LineOffset, MaskedDiscriminator,
6593164fcfdSRichard Smith BodySample.second.getSamples());
6603164fcfdSRichard Smith for (const auto &Target : BodySample.second.getCallTargets()) {
6613164fcfdSRichard Smith Result.addCalledTargetSamples(BodySample.first.LineOffset,
6628d581857SRong Xu MaskedDiscriminator,
6633164fcfdSRichard Smith Remapper(Target.first()), Target.second);
6643164fcfdSRichard Smith }
6653164fcfdSRichard Smith }
6663164fcfdSRichard Smith for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) {
6673164fcfdSRichard Smith sampleprof::FunctionSamplesMap &Target =
6683164fcfdSRichard Smith Result.functionSamplesAt(CallsiteSamples.first);
6693164fcfdSRichard Smith for (const auto &Callsite : CallsiteSamples.second) {
6703164fcfdSRichard Smith sampleprof::FunctionSamples Remapped =
6713164fcfdSRichard Smith remapSamples(Callsite.second, Remapper, Error);
672adcd0268SBenjamin Kramer MergeResult(Error,
673adcd0268SBenjamin Kramer Target[std::string(Remapped.getName())].merge(Remapped));
6743164fcfdSRichard Smith }
6753164fcfdSRichard Smith }
6763164fcfdSRichard Smith return Result;
6773164fcfdSRichard Smith }
6783164fcfdSRichard Smith
6796f7c19a4SXinliang David Li static sampleprof::SampleProfileFormat FormatMap[] = {
680be907324SWei Mi sampleprof::SPF_None,
681be907324SWei Mi sampleprof::SPF_Text,
682be907324SWei Mi sampleprof::SPF_Compact_Binary,
683be907324SWei Mi sampleprof::SPF_Ext_Binary,
684be907324SWei Mi sampleprof::SPF_GCC,
685be907324SWei Mi sampleprof::SPF_Binary};
6866f7c19a4SXinliang David Li
687798e59b8SWei Mi static std::unique_ptr<MemoryBuffer>
getInputFileBuf(const StringRef & InputFile)688798e59b8SWei Mi getInputFileBuf(const StringRef &InputFile) {
689798e59b8SWei Mi if (InputFile == "")
690798e59b8SWei Mi return {};
691798e59b8SWei Mi
692798e59b8SWei Mi auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);
693798e59b8SWei Mi if (!BufOrError)
694798e59b8SWei Mi exitWithErrorCode(BufOrError.getError(), InputFile);
695798e59b8SWei Mi
696798e59b8SWei Mi return std::move(*BufOrError);
697798e59b8SWei Mi }
698798e59b8SWei Mi
populateProfileSymbolList(MemoryBuffer * Buffer,sampleprof::ProfileSymbolList & PSL)699798e59b8SWei Mi static void populateProfileSymbolList(MemoryBuffer *Buffer,
700798e59b8SWei Mi sampleprof::ProfileSymbolList &PSL) {
701798e59b8SWei Mi if (!Buffer)
702798e59b8SWei Mi return;
703798e59b8SWei Mi
704798e59b8SWei Mi SmallVector<StringRef, 32> SymbolVec;
705798e59b8SWei Mi StringRef Data = Buffer->getBuffer();
706798e59b8SWei Mi Data.split(SymbolVec, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
707798e59b8SWei Mi
70891337e90SChris Bieneman for (StringRef SymbolStr : SymbolVec)
70991337e90SChris Bieneman PSL.add(SymbolStr.trim());
710798e59b8SWei Mi }
711798e59b8SWei Mi
handleExtBinaryWriter(sampleprof::SampleProfileWriter & Writer,ProfileFormat OutputFormat,MemoryBuffer * Buffer,sampleprof::ProfileSymbolList & WriterList,bool CompressAllSections,bool UseMD5,bool GenPartialProfile)712b523790aSWei Mi static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer,
713b523790aSWei Mi ProfileFormat OutputFormat,
714b523790aSWei Mi MemoryBuffer *Buffer,
715b523790aSWei Mi sampleprof::ProfileSymbolList &WriterList,
716b49eac71SWei Mi bool CompressAllSections, bool UseMD5,
71756926ae0SWei Mi bool GenPartialProfile) {
718b523790aSWei Mi populateProfileSymbolList(Buffer, WriterList);
719b523790aSWei Mi if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary)
720b523790aSWei Mi warn("Profile Symbol list is not empty but the output format is not "
721b523790aSWei Mi "ExtBinary format. The list will be lost in the output. ");
722b523790aSWei Mi
723b523790aSWei Mi Writer.setProfileSymbolList(&WriterList);
724b523790aSWei Mi
725b523790aSWei Mi if (CompressAllSections) {
726ebad6788SWei Mi if (OutputFormat != PF_Ext_Binary)
727b523790aSWei Mi warn("-compress-all-section is ignored. Specify -extbinary to enable it");
728ebad6788SWei Mi else
729ebad6788SWei Mi Writer.setToCompressAllSections();
730b523790aSWei Mi }
731ebad6788SWei Mi if (UseMD5) {
732ebad6788SWei Mi if (OutputFormat != PF_Ext_Binary)
733ebad6788SWei Mi warn("-use-md5 is ignored. Specify -extbinary to enable it");
734ebad6788SWei Mi else
735ebad6788SWei Mi Writer.setUseMD5();
736b523790aSWei Mi }
73756926ae0SWei Mi if (GenPartialProfile) {
738b49eac71SWei Mi if (OutputFormat != PF_Ext_Binary)
73956926ae0SWei Mi warn("-gen-partial-profile is ignored. Specify -extbinary to enable it");
740b49eac71SWei Mi else
741b49eac71SWei Mi Writer.setPartialProfile();
742b49eac71SWei Mi }
743b523790aSWei Mi }
744b523790aSWei Mi
745ebad6788SWei Mi static void
mergeSampleProfile(const WeightedFileVector & Inputs,SymbolRemapper * Remapper,StringRef OutputFilename,ProfileFormat OutputFormat,StringRef ProfileSymbolListFile,bool CompressAllSections,bool UseMD5,bool GenPartialProfile,bool GenCSNestedProfile,bool SampleMergeColdContext,bool SampleTrimColdContext,bool SampleColdContextFrameDepth,FailureMode FailMode)746ebad6788SWei Mi mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper,
747ebad6788SWei Mi StringRef OutputFilename, ProfileFormat OutputFormat,
748ebad6788SWei Mi StringRef ProfileSymbolListFile, bool CompressAllSections,
7495740bb80SHongtao Yu bool UseMD5, bool GenPartialProfile, bool GenCSNestedProfile,
750dff83158SWenlei He bool SampleMergeColdContext, bool SampleTrimColdContext,
751863184ddSwlei bool SampleColdContextFrameDepth, FailureMode FailMode) {
752d5336ae2SDiego Novillo using namespace sampleprof;
753b9db7036SHongtao Yu SampleProfileMap ProfileMap;
754aae1ed8eSDiego Novillo SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers;
75503b42e41SMehdi Amini LLVMContext Context;
756798e59b8SWei Mi sampleprof::ProfileSymbolList WriterList;
757ac068e01SHongtao Yu Optional<bool> ProfileIsProbeBased;
758e36786d1SHongtao Yu Optional<bool> ProfileIsCS;
7597f5b47ddSNathan Slingerland for (const auto &Input : Inputs) {
7608d581857SRong Xu auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context,
7618d581857SRong Xu FSDiscriminatorPassOption);
7620fcfe897SVedant Kumar if (std::error_code EC = ReaderOrErr.getError()) {
7630fcfe897SVedant Kumar warnOrExitGivenError(FailMode, EC, Input.Filename);
7640fcfe897SVedant Kumar continue;
7650fcfe897SVedant Kumar }
766d5336ae2SDiego Novillo
767aae1ed8eSDiego Novillo // We need to keep the readers around until after all the files are
768aae1ed8eSDiego Novillo // read so that we do not lose the function names stored in each
769aae1ed8eSDiego Novillo // reader's memory. The function names are needed to write out the
770aae1ed8eSDiego Novillo // merged profile map.
771aae1ed8eSDiego Novillo Readers.push_back(std::move(ReaderOrErr.get()));
772aae1ed8eSDiego Novillo const auto Reader = Readers.back().get();
7730fcfe897SVedant Kumar if (std::error_code EC = Reader->read()) {
7740fcfe897SVedant Kumar warnOrExitGivenError(FailMode, EC, Input.Filename);
7750fcfe897SVedant Kumar Readers.pop_back();
7760fcfe897SVedant Kumar continue;
7770fcfe897SVedant Kumar }
778d5336ae2SDiego Novillo
779b9db7036SHongtao Yu SampleProfileMap &Profiles = Reader->getProfiles();
780a7938c74SKazu Hirata if (ProfileIsProbeBased &&
781ac068e01SHongtao Yu ProfileIsProbeBased != FunctionSamples::ProfileIsProbeBased)
782ac068e01SHongtao Yu exitWithError(
783ac068e01SHongtao Yu "cannot merge probe-based profile with non-probe-based profile");
784ac068e01SHongtao Yu ProfileIsProbeBased = FunctionSamples::ProfileIsProbeBased;
785a7938c74SKazu Hirata if (ProfileIsCS && ProfileIsCS != FunctionSamples::ProfileIsCS)
786e68fafa4SHongtao Yu exitWithError("cannot merge CS profile with non-CS profile");
787e36786d1SHongtao Yu ProfileIsCS = FunctionSamples::ProfileIsCS;
788b9db7036SHongtao Yu for (SampleProfileMap::iterator I = Profiles.begin(), E = Profiles.end();
789d5336ae2SDiego Novillo I != E; ++I) {
7903164fcfdSRichard Smith sampleprof_error Result = sampleprof_error::success;
7913164fcfdSRichard Smith FunctionSamples Remapped =
7923164fcfdSRichard Smith Remapper ? remapSamples(I->second, *Remapper, Result)
7933164fcfdSRichard Smith : FunctionSamples();
7943164fcfdSRichard Smith FunctionSamples &Samples = Remapper ? Remapped : I->second;
795b9db7036SHongtao Yu SampleContext FContext = Samples.getContext();
796b9db7036SHongtao Yu MergeResult(Result, ProfileMap[FContext].merge(Samples, Input.Weight));
79748dd080cSNathan Slingerland if (Result != sampleprof_error::success) {
79848dd080cSNathan Slingerland std::error_code EC = make_error_code(Result);
799b9db7036SHongtao Yu handleMergeWriterError(errorCodeToError(EC), Input.Filename,
800b9db7036SHongtao Yu FContext.toString());
80148dd080cSNathan Slingerland }
802d5336ae2SDiego Novillo }
803798e59b8SWei Mi
804798e59b8SWei Mi std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =
805798e59b8SWei Mi Reader->getProfileSymbolList();
806798e59b8SWei Mi if (ReaderList)
807798e59b8SWei Mi WriterList.merge(*ReaderList);
808d5336ae2SDiego Novillo }
809dff83158SWenlei He
810e36786d1SHongtao Yu if (ProfileIsCS && (SampleMergeColdContext || SampleTrimColdContext)) {
811dff83158SWenlei He // Use threshold calculated from profile summary unless specified.
812dff83158SWenlei He SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
813dff83158SWenlei He auto Summary = Builder.computeSummaryForProfiles(ProfileMap);
814dff83158SWenlei He uint64_t SampleProfColdThreshold =
815dff83158SWenlei He ProfileSummaryBuilder::getColdCountThreshold(
816dff83158SWenlei He (Summary->getDetailedSummary()));
817dff83158SWenlei He
818dff83158SWenlei He // Trim and merge cold context profile using cold threshold above;
819dff83158SWenlei He SampleContextTrimmer(ProfileMap)
820863184ddSwlei .trimAndMergeColdContextProfiles(
821863184ddSwlei SampleProfColdThreshold, SampleTrimColdContext,
822259e4c56SHongtao Yu SampleMergeColdContext, SampleColdContextFrameDepth, false);
823dff83158SWenlei He }
824dff83158SWenlei He
825e36786d1SHongtao Yu if (ProfileIsCS && GenCSNestedProfile) {
8265740bb80SHongtao Yu CSProfileConverter CSConverter(ProfileMap);
8275740bb80SHongtao Yu CSConverter.convertProfiles();
828e36786d1SHongtao Yu ProfileIsCS = FunctionSamples::ProfileIsCS = false;
8295740bb80SHongtao Yu }
8305740bb80SHongtao Yu
831f0d3dcecSRong Xu auto WriterOrErr =
832f0d3dcecSRong Xu SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]);
833f0d3dcecSRong Xu if (std::error_code EC = WriterOrErr.getError())
834f0d3dcecSRong Xu exitWithErrorCode(EC, OutputFilename);
835f0d3dcecSRong Xu
836b523790aSWei Mi auto Writer = std::move(WriterOrErr.get());
837798e59b8SWei Mi // WriterList will have StringRef refering to string in Buffer.
838798e59b8SWei Mi // Make sure Buffer lives as long as WriterList.
839798e59b8SWei Mi auto Buffer = getInputFileBuf(ProfileSymbolListFile);
840b523790aSWei Mi handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList,
84156926ae0SWei Mi CompressAllSections, UseMD5, GenPartialProfile);
84200ef28efSWenlei He if (std::error_code EC = Writer->write(ProfileMap))
84300ef28efSWenlei He exitWithErrorCode(std::move(EC));
844d5336ae2SDiego Novillo }
845d5336ae2SDiego Novillo
parseWeightedFile(const StringRef & WeightedFilename)8467f5b47ddSNathan Slingerland static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) {
8478d0e861eSVedant Kumar StringRef WeightStr, FileName;
8488d0e861eSVedant Kumar std::tie(WeightStr, FileName) = WeightedFilename.split(',');
849d5336ae2SDiego Novillo
8507f5b47ddSNathan Slingerland uint64_t Weight;
8517f5b47ddSNathan Slingerland if (WeightStr.getAsInteger(10, Weight) || Weight < 1)
8528ea2a58aSFangrui Song exitWithError("input weight must be a positive integer");
8537f5b47ddSNathan Slingerland
854adcd0268SBenjamin Kramer return {std::string(FileName), Weight};
8557f5b47ddSNathan Slingerland }
8567f5b47ddSNathan Slingerland
addWeightedInput(WeightedFileVector & WNI,const WeightedFile & WF)8579a1bfcfaSXinliang David Li static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) {
8589a1bfcfaSXinliang David Li StringRef Filename = WF.Filename;
8599a1bfcfaSXinliang David Li uint64_t Weight = WF.Weight;
860a81f4728SBenjamin Kramer
861a81f4728SBenjamin Kramer // If it's STDIN just pass it on.
862a81f4728SBenjamin Kramer if (Filename == "-") {
863adcd0268SBenjamin Kramer WNI.push_back({std::string(Filename), Weight});
864a81f4728SBenjamin Kramer return;
865a81f4728SBenjamin Kramer }
866a81f4728SBenjamin Kramer
8679a1bfcfaSXinliang David Li llvm::sys::fs::file_status Status;
8689a1bfcfaSXinliang David Li llvm::sys::fs::status(Filename, Status);
8699a1bfcfaSXinliang David Li if (!llvm::sys::fs::exists(Status))
8709a1bfcfaSXinliang David Li exitWithErrorCode(make_error_code(errc::no_such_file_or_directory),
8719a1bfcfaSXinliang David Li Filename);
8729a1bfcfaSXinliang David Li // If it's a source file, collect it.
8739a1bfcfaSXinliang David Li if (llvm::sys::fs::is_regular_file(Status)) {
874adcd0268SBenjamin Kramer WNI.push_back({std::string(Filename), Weight});
8759a1bfcfaSXinliang David Li return;
8769a1bfcfaSXinliang David Li }
8779a1bfcfaSXinliang David Li
8789a1bfcfaSXinliang David Li if (llvm::sys::fs::is_directory(Status)) {
8799a1bfcfaSXinliang David Li std::error_code EC;
8809a1bfcfaSXinliang David Li for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E;
8819a1bfcfaSXinliang David Li F != E && !EC; F.increment(EC)) {
8829a1bfcfaSXinliang David Li if (llvm::sys::fs::is_regular_file(F->path())) {
8839a1bfcfaSXinliang David Li addWeightedInput(WNI, {F->path(), Weight});
8849a1bfcfaSXinliang David Li }
8859a1bfcfaSXinliang David Li }
8869a1bfcfaSXinliang David Li if (EC)
8879a1bfcfaSXinliang David Li exitWithErrorCode(EC, Filename);
8889a1bfcfaSXinliang David Li }
8899a1bfcfaSXinliang David Li }
8909a1bfcfaSXinliang David Li
parseInputFilenamesFile(MemoryBuffer * Buffer,WeightedFileVector & WFV)891cef4360aSVedant Kumar static void parseInputFilenamesFile(MemoryBuffer *Buffer,
892cef4360aSVedant Kumar WeightedFileVector &WFV) {
893cef4360aSVedant Kumar if (!Buffer)
894cef4360aSVedant Kumar return;
895cef4360aSVedant Kumar
896cef4360aSVedant Kumar SmallVector<StringRef, 8> Entries;
897cef4360aSVedant Kumar StringRef Data = Buffer->getBuffer();
898cef4360aSVedant Kumar Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
899cef4360aSVedant Kumar for (const StringRef &FileWeightEntry : Entries) {
900cef4360aSVedant Kumar StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r");
901cef4360aSVedant Kumar // Skip comments.
902cef4360aSVedant Kumar if (SanitizedEntry.startswith("#"))
903cef4360aSVedant Kumar continue;
904cef4360aSVedant Kumar // If there's no comma, it's an unweighted profile.
9054e3eebc6SKazu Hirata else if (!SanitizedEntry.contains(','))
906adcd0268SBenjamin Kramer addWeightedInput(WFV, {std::string(SanitizedEntry), 1});
907cef4360aSVedant Kumar else
9089a1bfcfaSXinliang David Li addWeightedInput(WFV, parseWeightedFile(SanitizedEntry));
909cef4360aSVedant Kumar }
910cef4360aSVedant Kumar }
911cef4360aSVedant Kumar
merge_main(int argc,const char * argv[])9127f5b47ddSNathan Slingerland static int merge_main(int argc, const char *argv[]) {
9137f5b47ddSNathan Slingerland cl::list<std::string> InputFilenames(cl::Positional,
9147f5b47ddSNathan Slingerland cl::desc("<filename...>"));
9157f5b47ddSNathan Slingerland cl::list<std::string> WeightedInputFilenames("weighted-input",
9167f5b47ddSNathan Slingerland cl::desc("<weight>,<filename>"));
917cef4360aSVedant Kumar cl::opt<std::string> InputFilenamesFile(
918cef4360aSVedant Kumar "input-files", cl::init(""),
919cef4360aSVedant Kumar cl::desc("Path to file containing newline-separated "
920cef4360aSVedant Kumar "[<weight>,]<filename> entries"));
921cef4360aSVedant Kumar cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"),
922cef4360aSVedant Kumar cl::aliasopt(InputFilenamesFile));
923cef4360aSVedant Kumar cl::opt<bool> DumpInputFileList(
924cef4360aSVedant Kumar "dump-input-file-list", cl::init(false), cl::Hidden,
925cef4360aSVedant Kumar cl::desc("Dump the list of input files and their weights, then exit"));
9263164fcfdSRichard Smith cl::opt<std::string> RemappingFile("remapping-file", cl::value_desc("file"),
9273164fcfdSRichard Smith cl::desc("Symbol remapping file"));
9283164fcfdSRichard Smith cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"),
9293164fcfdSRichard Smith cl::aliasopt(RemappingFile));
930d5336ae2SDiego Novillo cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
931ea23c38dSFangrui Song cl::init("-"), cl::desc("Output file"));
932d5336ae2SDiego Novillo cl::alias OutputFilenameA("o", cl::desc("Alias for --output"),
933d5336ae2SDiego Novillo cl::aliasopt(OutputFilename));
934d5336ae2SDiego Novillo cl::opt<ProfileKinds> ProfileKind(
935d5336ae2SDiego Novillo cl::desc("Profile kind:"), cl::init(instr),
936d5336ae2SDiego Novillo cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
937732afdd0SMehdi Amini clEnumVal(sample, "Sample profile")));
9386f7c19a4SXinliang David Li cl::opt<ProfileFormat> OutputFormat(
939d9be2c7eSWei Mi cl::desc("Format of output profile"), cl::init(PF_Binary),
940be907324SWei Mi cl::values(
941be907324SWei Mi clEnumValN(PF_Binary, "binary", "Binary encoding (default)"),
942a0c0857eSWei Mi clEnumValN(PF_Compact_Binary, "compbinary",
943432db3b4SWei Mi "Compact binary encoding"),
944be907324SWei Mi clEnumValN(PF_Ext_Binary, "extbinary", "Extensible binary encoding"),
9456f7c19a4SXinliang David Li clEnumValN(PF_Text, "text", "Text encoding"),
9466f7c19a4SXinliang David Li clEnumValN(PF_GCC, "gcc",
947732afdd0SMehdi Amini "GCC encoding (only meaningful for -sample)")));
9480fcfe897SVedant Kumar cl::opt<FailureMode> FailureMode(
9490fcfe897SVedant Kumar "failure-mode", cl::init(failIfAnyAreInvalid), cl::desc("Failure mode:"),
9500fcfe897SVedant Kumar cl::values(clEnumValN(failIfAnyAreInvalid, "any",
9510fcfe897SVedant Kumar "Fail if any profile is invalid."),
9520fcfe897SVedant Kumar clEnumValN(failIfAllAreInvalid, "all",
9530fcfe897SVedant Kumar "Fail only if all profiles are invalid.")));
95400dab228SVedant Kumar cl::opt<bool> OutputSparse("sparse", cl::init(false),
95500dab228SVedant Kumar cl::desc("Generate a sparse profile (only meaningful for -instr)"));
956e3a0bf50SVedant Kumar cl::opt<unsigned> NumThreads(
957e3a0bf50SVedant Kumar "num-threads", cl::init(0),
958e3a0bf50SVedant Kumar cl::desc("Number of merge threads to use (default: autodetect)"));
959e3a0bf50SVedant Kumar cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
960e3a0bf50SVedant Kumar cl::aliasopt(NumThreads));
961798e59b8SWei Mi cl::opt<std::string> ProfileSymbolListFile(
962798e59b8SWei Mi "prof-sym-list", cl::init(""),
963798e59b8SWei Mi cl::desc("Path to file containing the list of function symbols "
964798e59b8SWei Mi "used to populate profile symbol list"));
965b523790aSWei Mi cl::opt<bool> CompressAllSections(
966b523790aSWei Mi "compress-all-sections", cl::init(false), cl::Hidden,
967b523790aSWei Mi cl::desc("Compress all sections when writing the profile (only "
968b523790aSWei Mi "meaningful for -extbinary)"));
969ebad6788SWei Mi cl::opt<bool> UseMD5(
970ebad6788SWei Mi "use-md5", cl::init(false), cl::Hidden,
971ebad6788SWei Mi cl::desc("Choose to use MD5 to represent string in name table (only "
972ebad6788SWei Mi "meaningful for -extbinary)"));
973dff83158SWenlei He cl::opt<bool> SampleMergeColdContext(
974dff83158SWenlei He "sample-merge-cold-context", cl::init(false), cl::Hidden,
975dff83158SWenlei He cl::desc(
976dff83158SWenlei He "Merge context sample profiles whose count is below cold threshold"));
977dff83158SWenlei He cl::opt<bool> SampleTrimColdContext(
978dff83158SWenlei He "sample-trim-cold-context", cl::init(false), cl::Hidden,
979dff83158SWenlei He cl::desc(
980dff83158SWenlei He "Trim context sample profiles whose count is below cold threshold"));
981863184ddSwlei cl::opt<uint32_t> SampleColdContextFrameDepth(
982557efc9aSFangrui Song "sample-frame-depth-for-cold-context", cl::init(1),
983863184ddSwlei cl::desc("Keep the last K frames while merging cold profile. 1 means the "
984863184ddSwlei "context-less base profile"));
98556926ae0SWei Mi cl::opt<bool> GenPartialProfile(
98656926ae0SWei Mi "gen-partial-profile", cl::init(false), cl::Hidden,
98756926ae0SWei Mi cl::desc("Generate a partial profile (only meaningful for -extbinary)"));
988a23f6234SWei Mi cl::opt<std::string> SupplInstrWithSample(
989a23f6234SWei Mi "supplement-instr-with-sample", cl::init(""), cl::Hidden,
990a23f6234SWei Mi cl::desc("Supplement an instr profile with sample profile, to correct "
991a23f6234SWei Mi "the profile unrepresentativeness issue. The sample "
992a23f6234SWei Mi "profile is the input of the flag. Output will be in instr "
993a23f6234SWei Mi "format (The flag only works with -instr)"));
994a23f6234SWei Mi cl::opt<float> ZeroCounterThreshold(
995a23f6234SWei Mi "zero-counter-threshold", cl::init(0.7), cl::Hidden,
996a23f6234SWei Mi cl::desc("For the function which is cold in instr profile but hot in "
997a23f6234SWei Mi "sample profile, if the ratio of the number of zero counters "
99887a55137SBrian Tracy "divided by the total number of counters is above the "
999a23f6234SWei Mi "threshold, the profile of the function will be regarded as "
1000a23f6234SWei Mi "being harmful for performance and will be dropped."));
1001a23f6234SWei Mi cl::opt<unsigned> SupplMinSizeThreshold(
1002a23f6234SWei Mi "suppl-min-size-threshold", cl::init(10), cl::Hidden,
1003a23f6234SWei Mi cl::desc("If the size of a function is smaller than the threshold, "
1004a23f6234SWei Mi "assume it can be inlined by PGO early inliner and it won't "
1005a23f6234SWei Mi "be adjusted based on sample profile."));
1006a23f6234SWei Mi cl::opt<unsigned> InstrProfColdThreshold(
1007a23f6234SWei Mi "instr-prof-cold-threshold", cl::init(0), cl::Hidden,
1008a23f6234SWei Mi cl::desc("User specified cold threshold for instr profile which will "
1009a23f6234SWei Mi "override the cold threshold got from profile summary. "));
10105740bb80SHongtao Yu cl::opt<bool> GenCSNestedProfile(
10115740bb80SHongtao Yu "gen-cs-nested-profile", cl::Hidden, cl::init(false),
10125740bb80SHongtao Yu cl::desc("Generate nested function profiles for CSSPGO"));
101365d7fd02SEllis Hoag cl::opt<std::string> DebugInfoFilename(
10144ecf15b7SKyungwoo Lee "debug-info", cl::init(""),
101565d7fd02SEllis Hoag cl::desc("Use the provided debug info to correlate the raw profile."));
10160a418490SSnehasish Kumar cl::opt<std::string> ProfiledBinary(
10170a418490SSnehasish Kumar "profiled-binary", cl::init(""),
10180a418490SSnehasish Kumar cl::desc("Path to binary from which the profile was collected."));
101900dab228SVedant Kumar
1020d5336ae2SDiego Novillo cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n");
1021d5336ae2SDiego Novillo
1022cef4360aSVedant Kumar WeightedFileVector WeightedInputs;
1023cef4360aSVedant Kumar for (StringRef Filename : InputFilenames)
1024adcd0268SBenjamin Kramer addWeightedInput(WeightedInputs, {std::string(Filename), 1});
1025cef4360aSVedant Kumar for (StringRef WeightedFilename : WeightedInputFilenames)
10269a1bfcfaSXinliang David Li addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename));
1027cef4360aSVedant Kumar
1028cef4360aSVedant Kumar // Make sure that the file buffer stays alive for the duration of the
1029cef4360aSVedant Kumar // weighted input vector's lifetime.
1030798e59b8SWei Mi auto Buffer = getInputFileBuf(InputFilenamesFile);
1031cef4360aSVedant Kumar parseInputFilenamesFile(Buffer.get(), WeightedInputs);
1032cef4360aSVedant Kumar
1033cef4360aSVedant Kumar if (WeightedInputs.empty())
10348ea2a58aSFangrui Song exitWithError("no input files specified. See " +
10350c30f89cSChandler Carruth sys::path::filename(argv[0]) + " -help");
10360c30f89cSChandler Carruth
1037cef4360aSVedant Kumar if (DumpInputFileList) {
1038cef4360aSVedant Kumar for (auto &WF : WeightedInputs)
1039cef4360aSVedant Kumar outs() << WF.Weight << "," << WF.Filename << "\n";
1040cef4360aSVedant Kumar return 0;
1041cef4360aSVedant Kumar }
1042f771a050SVedant Kumar
10433164fcfdSRichard Smith std::unique_ptr<SymbolRemapper> Remapper;
10443164fcfdSRichard Smith if (!RemappingFile.empty())
10453164fcfdSRichard Smith Remapper = SymbolRemapper::create(RemappingFile);
10463164fcfdSRichard Smith
1047a23f6234SWei Mi if (!SupplInstrWithSample.empty()) {
1048a23f6234SWei Mi if (ProfileKind != instr)
1049a23f6234SWei Mi exitWithError(
1050a23f6234SWei Mi "-supplement-instr-with-sample can only work with -instr. ");
1051a23f6234SWei Mi
1052a23f6234SWei Mi supplementInstrProfile(WeightedInputs, SupplInstrWithSample, OutputFilename,
1053a23f6234SWei Mi OutputFormat, OutputSparse, SupplMinSizeThreshold,
1054a23f6234SWei Mi ZeroCounterThreshold, InstrProfColdThreshold);
1055a23f6234SWei Mi return 0;
1056a23f6234SWei Mi }
1057a23f6234SWei Mi
1058d5336ae2SDiego Novillo if (ProfileKind == instr)
105965d7fd02SEllis Hoag mergeInstrProfile(WeightedInputs, DebugInfoFilename, Remapper.get(),
106065d7fd02SEllis Hoag OutputFilename, OutputFormat, OutputSparse, NumThreads,
10610a418490SSnehasish Kumar FailureMode, ProfiledBinary);
1062d5336ae2SDiego Novillo else
10633164fcfdSRichard Smith mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename,
1064b523790aSWei Mi OutputFormat, ProfileSymbolListFile, CompressAllSections,
10655740bb80SHongtao Yu UseMD5, GenPartialProfile, GenCSNestedProfile,
10665740bb80SHongtao Yu SampleMergeColdContext, SampleTrimColdContext,
10675740bb80SHongtao Yu SampleColdContextFrameDepth, FailureMode);
1068bfee8d49SJustin Bogner return 0;
1069bfee8d49SJustin Bogner }
1070618bcea7SJustin Bogner
1071998b97f6SRong Xu /// Computer the overlap b/w profile BaseFilename and profile TestFilename.
overlapInstrProfile(const std::string & BaseFilename,const std::string & TestFilename,const OverlapFuncFilters & FuncFilter,raw_fd_ostream & OS,bool IsCS)1072998b97f6SRong Xu static void overlapInstrProfile(const std::string &BaseFilename,
1073998b97f6SRong Xu const std::string &TestFilename,
1074998b97f6SRong Xu const OverlapFuncFilters &FuncFilter,
1075998b97f6SRong Xu raw_fd_ostream &OS, bool IsCS) {
1076998b97f6SRong Xu std::mutex ErrorLock;
1077998b97f6SRong Xu SmallSet<instrprof_error, 4> WriterErrorCodes;
1078998b97f6SRong Xu WriterContext Context(false, ErrorLock, WriterErrorCodes);
1079998b97f6SRong Xu WeightedFile WeightedInput{BaseFilename, 1};
1080998b97f6SRong Xu OverlapStats Overlap;
1081e0fa2689SRong Xu Error E = Overlap.accumulateCounts(BaseFilename, TestFilename, IsCS);
1082998b97f6SRong Xu if (E)
10838ea2a58aSFangrui Song exitWithError(std::move(E), "error in getting profile count sums");
1084998b97f6SRong Xu if (Overlap.Base.CountSum < 1.0f) {
1085998b97f6SRong Xu OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n";
1086998b97f6SRong Xu exit(0);
1087998b97f6SRong Xu }
1088998b97f6SRong Xu if (Overlap.Test.CountSum < 1.0f) {
1089998b97f6SRong Xu OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n";
1090998b97f6SRong Xu exit(0);
1091998b97f6SRong Xu }
10920a418490SSnehasish Kumar loadInput(WeightedInput, nullptr, nullptr, /*ProfiledBinary=*/"", &Context);
1093998b97f6SRong Xu overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS,
1094998b97f6SRong Xu IsCS);
1095998b97f6SRong Xu Overlap.dump(OS);
1096998b97f6SRong Xu }
1097998b97f6SRong Xu
1098540489deSweihe namespace {
1099540489deSweihe struct SampleOverlapStats {
1100b9db7036SHongtao Yu SampleContext BaseName;
1101b9db7036SHongtao Yu SampleContext TestName;
1102540489deSweihe // Number of overlap units
1103540489deSweihe uint64_t OverlapCount;
1104540489deSweihe // Total samples of overlap units
1105540489deSweihe uint64_t OverlapSample;
1106540489deSweihe // Number of and total samples of units that only present in base or test
1107540489deSweihe // profile
1108540489deSweihe uint64_t BaseUniqueCount;
1109540489deSweihe uint64_t BaseUniqueSample;
1110540489deSweihe uint64_t TestUniqueCount;
1111540489deSweihe uint64_t TestUniqueSample;
1112540489deSweihe // Number of units and total samples in base or test profile
1113540489deSweihe uint64_t BaseCount;
1114540489deSweihe uint64_t BaseSample;
1115540489deSweihe uint64_t TestCount;
1116540489deSweihe uint64_t TestSample;
1117540489deSweihe // Number of and total samples of units that present in at least one profile
1118540489deSweihe uint64_t UnionCount;
1119540489deSweihe uint64_t UnionSample;
1120540489deSweihe // Weighted similarity
1121540489deSweihe double Similarity;
1122540489deSweihe // For SampleOverlapStats instances representing functions, weights of the
1123540489deSweihe // function in base and test profiles
1124540489deSweihe double BaseWeight;
1125540489deSweihe double TestWeight;
1126540489deSweihe
SampleOverlapStats__anon73ab2c7b0a11::SampleOverlapStats1127540489deSweihe SampleOverlapStats()
1128540489deSweihe : OverlapCount(0), OverlapSample(0), BaseUniqueCount(0),
1129540489deSweihe BaseUniqueSample(0), TestUniqueCount(0), TestUniqueSample(0),
1130540489deSweihe BaseCount(0), BaseSample(0), TestCount(0), TestSample(0), UnionCount(0),
1131540489deSweihe UnionSample(0), Similarity(0.0), BaseWeight(0.0), TestWeight(0.0) {}
1132540489deSweihe };
1133540489deSweihe } // end anonymous namespace
1134540489deSweihe
1135540489deSweihe namespace {
1136540489deSweihe struct FuncSampleStats {
1137540489deSweihe uint64_t SampleSum;
1138540489deSweihe uint64_t MaxSample;
1139540489deSweihe uint64_t HotBlockCount;
FuncSampleStats__anon73ab2c7b0b11::FuncSampleStats1140540489deSweihe FuncSampleStats() : SampleSum(0), MaxSample(0), HotBlockCount(0) {}
FuncSampleStats__anon73ab2c7b0b11::FuncSampleStats1141540489deSweihe FuncSampleStats(uint64_t SampleSum, uint64_t MaxSample,
1142540489deSweihe uint64_t HotBlockCount)
1143540489deSweihe : SampleSum(SampleSum), MaxSample(MaxSample),
1144540489deSweihe HotBlockCount(HotBlockCount) {}
1145540489deSweihe };
1146540489deSweihe } // end anonymous namespace
1147540489deSweihe
1148540489deSweihe namespace {
1149540489deSweihe enum MatchStatus { MS_Match, MS_FirstUnique, MS_SecondUnique, MS_None };
1150540489deSweihe
1151540489deSweihe // Class for updating merging steps for two sorted maps. The class should be
1152540489deSweihe // instantiated with a map iterator type.
1153540489deSweihe template <class T> class MatchStep {
1154540489deSweihe public:
1155540489deSweihe MatchStep() = delete;
1156540489deSweihe
MatchStep(T FirstIter,T FirstEnd,T SecondIter,T SecondEnd)1157540489deSweihe MatchStep(T FirstIter, T FirstEnd, T SecondIter, T SecondEnd)
1158540489deSweihe : FirstIter(FirstIter), FirstEnd(FirstEnd), SecondIter(SecondIter),
1159540489deSweihe SecondEnd(SecondEnd), Status(MS_None) {}
1160540489deSweihe
areBothFinished() const1161540489deSweihe bool areBothFinished() const {
1162540489deSweihe return (FirstIter == FirstEnd && SecondIter == SecondEnd);
1163540489deSweihe }
1164540489deSweihe
isFirstFinished() const1165540489deSweihe bool isFirstFinished() const { return FirstIter == FirstEnd; }
1166540489deSweihe
isSecondFinished() const1167540489deSweihe bool isSecondFinished() const { return SecondIter == SecondEnd; }
1168540489deSweihe
1169540489deSweihe /// Advance one step based on the previous match status unless the previous
1170540489deSweihe /// status is MS_None. Then update Status based on the comparison between two
1171540489deSweihe /// container iterators at the current step. If the previous status is
1172540489deSweihe /// MS_None, it means two iterators are at the beginning and no comparison has
1173540489deSweihe /// been made, so we simply update Status without advancing the iterators.
1174540489deSweihe void updateOneStep();
1175540489deSweihe
getFirstIter() const1176540489deSweihe T getFirstIter() const { return FirstIter; }
1177540489deSweihe
getSecondIter() const1178540489deSweihe T getSecondIter() const { return SecondIter; }
1179540489deSweihe
getMatchStatus() const1180540489deSweihe MatchStatus getMatchStatus() const { return Status; }
1181540489deSweihe
1182540489deSweihe private:
1183540489deSweihe // Current iterator and end iterator of the first container.
1184540489deSweihe T FirstIter;
1185540489deSweihe T FirstEnd;
1186540489deSweihe // Current iterator and end iterator of the second container.
1187540489deSweihe T SecondIter;
1188540489deSweihe T SecondEnd;
1189540489deSweihe // Match status of the current step.
1190540489deSweihe MatchStatus Status;
1191540489deSweihe };
1192540489deSweihe } // end anonymous namespace
1193540489deSweihe
updateOneStep()1194540489deSweihe template <class T> void MatchStep<T>::updateOneStep() {
1195540489deSweihe switch (Status) {
1196540489deSweihe case MS_Match:
1197540489deSweihe ++FirstIter;
1198540489deSweihe ++SecondIter;
1199540489deSweihe break;
1200540489deSweihe case MS_FirstUnique:
1201540489deSweihe ++FirstIter;
1202540489deSweihe break;
1203540489deSweihe case MS_SecondUnique:
1204540489deSweihe ++SecondIter;
1205540489deSweihe break;
1206540489deSweihe case MS_None:
1207540489deSweihe break;
1208540489deSweihe }
1209540489deSweihe
1210540489deSweihe // Update Status according to iterators at the current step.
1211540489deSweihe if (areBothFinished())
1212540489deSweihe return;
1213540489deSweihe if (FirstIter != FirstEnd &&
1214540489deSweihe (SecondIter == SecondEnd || FirstIter->first < SecondIter->first))
1215540489deSweihe Status = MS_FirstUnique;
1216540489deSweihe else if (SecondIter != SecondEnd &&
1217540489deSweihe (FirstIter == FirstEnd || SecondIter->first < FirstIter->first))
1218540489deSweihe Status = MS_SecondUnique;
1219540489deSweihe else
1220540489deSweihe Status = MS_Match;
1221540489deSweihe }
1222540489deSweihe
1223540489deSweihe // Return the sum of line/block samples, the max line/block sample, and the
1224540489deSweihe // number of line/block samples above the given threshold in a function
1225540489deSweihe // including its inlinees.
getFuncSampleStats(const sampleprof::FunctionSamples & Func,FuncSampleStats & FuncStats,uint64_t HotThreshold)1226540489deSweihe static void getFuncSampleStats(const sampleprof::FunctionSamples &Func,
1227540489deSweihe FuncSampleStats &FuncStats,
1228540489deSweihe uint64_t HotThreshold) {
1229540489deSweihe for (const auto &L : Func.getBodySamples()) {
1230540489deSweihe uint64_t Sample = L.second.getSamples();
1231540489deSweihe FuncStats.SampleSum += Sample;
1232540489deSweihe FuncStats.MaxSample = std::max(FuncStats.MaxSample, Sample);
1233540489deSweihe if (Sample >= HotThreshold)
1234540489deSweihe ++FuncStats.HotBlockCount;
1235540489deSweihe }
1236540489deSweihe
1237540489deSweihe for (const auto &C : Func.getCallsiteSamples()) {
1238540489deSweihe for (const auto &F : C.second)
1239540489deSweihe getFuncSampleStats(F.second, FuncStats, HotThreshold);
1240540489deSweihe }
1241540489deSweihe }
1242540489deSweihe
1243540489deSweihe /// Predicate that determines if a function is hot with a given threshold. We
1244540489deSweihe /// keep it separate from its callsites for possible extension in the future.
isFunctionHot(const FuncSampleStats & FuncStats,uint64_t HotThreshold)1245540489deSweihe static bool isFunctionHot(const FuncSampleStats &FuncStats,
1246540489deSweihe uint64_t HotThreshold) {
1247540489deSweihe // We intentionally compare the maximum sample count in a function with the
1248540489deSweihe // HotThreshold to get an approximate determination on hot functions.
1249540489deSweihe return (FuncStats.MaxSample >= HotThreshold);
1250540489deSweihe }
1251540489deSweihe
1252540489deSweihe namespace {
1253540489deSweihe class SampleOverlapAggregator {
1254540489deSweihe public:
SampleOverlapAggregator(const std::string & BaseFilename,const std::string & TestFilename,double LowSimilarityThreshold,double Epsilon,const OverlapFuncFilters & FuncFilter)1255540489deSweihe SampleOverlapAggregator(const std::string &BaseFilename,
1256540489deSweihe const std::string &TestFilename,
1257540489deSweihe double LowSimilarityThreshold, double Epsilon,
1258540489deSweihe const OverlapFuncFilters &FuncFilter)
1259540489deSweihe : BaseFilename(BaseFilename), TestFilename(TestFilename),
1260540489deSweihe LowSimilarityThreshold(LowSimilarityThreshold), Epsilon(Epsilon),
1261540489deSweihe FuncFilter(FuncFilter) {}
1262540489deSweihe
1263540489deSweihe /// Detect 0-sample input profile and report to output stream. This interface
1264540489deSweihe /// should be called after loadProfiles().
1265540489deSweihe bool detectZeroSampleProfile(raw_fd_ostream &OS) const;
1266540489deSweihe
1267540489deSweihe /// Write out function-level similarity statistics for functions specified by
1268540489deSweihe /// options --function, --value-cutoff, and --similarity-cutoff.
1269540489deSweihe void dumpFuncSimilarity(raw_fd_ostream &OS) const;
1270540489deSweihe
1271540489deSweihe /// Write out program-level similarity and overlap statistics.
1272540489deSweihe void dumpProgramSummary(raw_fd_ostream &OS) const;
1273540489deSweihe
1274540489deSweihe /// Write out hot-function and hot-block statistics for base_profile,
1275540489deSweihe /// test_profile, and their overlap. For both cases, the overlap HO is
1276540489deSweihe /// calculated as follows:
1277540489deSweihe /// Given the number of functions (or blocks) that are hot in both profiles
1278540489deSweihe /// HCommon and the number of functions (or blocks) that are hot in at
1279540489deSweihe /// least one profile HUnion, HO = HCommon / HUnion.
1280540489deSweihe void dumpHotFuncAndBlockOverlap(raw_fd_ostream &OS) const;
1281540489deSweihe
1282540489deSweihe /// This function tries matching functions in base and test profiles. For each
1283540489deSweihe /// pair of matched functions, it aggregates the function-level
1284540489deSweihe /// similarity into a profile-level similarity. It also dump function-level
1285540489deSweihe /// similarity information of functions specified by --function,
1286540489deSweihe /// --value-cutoff, and --similarity-cutoff options. The program-level
1287540489deSweihe /// similarity PS is computed as follows:
1288540489deSweihe /// Given function-level similarity FS(A) for all function A, the
1289540489deSweihe /// weight of function A in base profile WB(A), and the weight of function
1290540489deSweihe /// A in test profile WT(A), compute PS(base_profile, test_profile) =
1291540489deSweihe /// sum_A(FS(A) * avg(WB(A), WT(A))) ranging in [0.0f to 1.0f] with 0.0
1292540489deSweihe /// meaning no-overlap.
1293540489deSweihe void computeSampleProfileOverlap(raw_fd_ostream &OS);
1294540489deSweihe
1295540489deSweihe /// Initialize ProfOverlap with the sum of samples in base and test
1296540489deSweihe /// profiles. This function also computes and keeps the sum of samples and
1297540489deSweihe /// max sample counts of each function in BaseStats and TestStats for later
1298540489deSweihe /// use to avoid re-computations.
1299540489deSweihe void initializeSampleProfileOverlap();
1300540489deSweihe
1301540489deSweihe /// Load profiles specified by BaseFilename and TestFilename.
1302540489deSweihe std::error_code loadProfiles();
1303540489deSweihe
1304b9db7036SHongtao Yu using FuncSampleStatsMap =
1305b9db7036SHongtao Yu std::unordered_map<SampleContext, FuncSampleStats, SampleContext::Hash>;
1306b9db7036SHongtao Yu
1307540489deSweihe private:
1308540489deSweihe SampleOverlapStats ProfOverlap;
1309540489deSweihe SampleOverlapStats HotFuncOverlap;
1310540489deSweihe SampleOverlapStats HotBlockOverlap;
1311540489deSweihe std::string BaseFilename;
1312540489deSweihe std::string TestFilename;
1313540489deSweihe std::unique_ptr<sampleprof::SampleProfileReader> BaseReader;
1314540489deSweihe std::unique_ptr<sampleprof::SampleProfileReader> TestReader;
1315540489deSweihe // BaseStats and TestStats hold FuncSampleStats for each function, with
1316540489deSweihe // function name as the key.
1317b9db7036SHongtao Yu FuncSampleStatsMap BaseStats;
1318b9db7036SHongtao Yu FuncSampleStatsMap TestStats;
1319540489deSweihe // Low similarity threshold in floating point number
1320540489deSweihe double LowSimilarityThreshold;
1321540489deSweihe // Block samples above BaseHotThreshold or TestHotThreshold are considered hot
1322540489deSweihe // for tracking hot blocks.
1323540489deSweihe uint64_t BaseHotThreshold;
1324540489deSweihe uint64_t TestHotThreshold;
1325540489deSweihe // A small threshold used to round the results of floating point accumulations
1326540489deSweihe // to resolve imprecision.
1327540489deSweihe const double Epsilon;
1328540489deSweihe std::multimap<double, SampleOverlapStats, std::greater<double>>
1329540489deSweihe FuncSimilarityDump;
1330540489deSweihe // FuncFilter carries specifications in options --value-cutoff and
1331540489deSweihe // --function.
1332540489deSweihe OverlapFuncFilters FuncFilter;
1333540489deSweihe // Column offsets for printing the function-level details table.
1334540489deSweihe static const unsigned int TestWeightCol = 15;
1335540489deSweihe static const unsigned int SimilarityCol = 30;
1336540489deSweihe static const unsigned int OverlapCol = 43;
1337540489deSweihe static const unsigned int BaseUniqueCol = 53;
1338540489deSweihe static const unsigned int TestUniqueCol = 67;
1339540489deSweihe static const unsigned int BaseSampleCol = 81;
1340540489deSweihe static const unsigned int TestSampleCol = 96;
1341540489deSweihe static const unsigned int FuncNameCol = 111;
1342540489deSweihe
1343540489deSweihe /// Return a similarity of two line/block sample counters in the same
1344540489deSweihe /// function in base and test profiles. The line/block-similarity BS(i) is
1345540489deSweihe /// computed as follows:
1346540489deSweihe /// For an offsets i, given the sample count at i in base profile BB(i),
1347540489deSweihe /// the sample count at i in test profile BT(i), the sum of sample counts
1348540489deSweihe /// in this function in base profile SB, and the sum of sample counts in
1349540489deSweihe /// this function in test profile ST, compute BS(i) = 1.0 - fabs(BB(i)/SB -
1350540489deSweihe /// BT(i)/ST), ranging in [0.0f to 1.0f] with 0.0 meaning no-overlap.
1351540489deSweihe double computeBlockSimilarity(uint64_t BaseSample, uint64_t TestSample,
1352540489deSweihe const SampleOverlapStats &FuncOverlap) const;
1353540489deSweihe
1354540489deSweihe void updateHotBlockOverlap(uint64_t BaseSample, uint64_t TestSample,
1355540489deSweihe uint64_t HotBlockCount);
1356540489deSweihe
1357b9db7036SHongtao Yu void getHotFunctions(const FuncSampleStatsMap &ProfStats,
1358b9db7036SHongtao Yu FuncSampleStatsMap &HotFunc,
1359540489deSweihe uint64_t HotThreshold) const;
1360540489deSweihe
1361540489deSweihe void computeHotFuncOverlap();
1362540489deSweihe
1363540489deSweihe /// This function updates statistics in FuncOverlap, HotBlockOverlap, and
1364540489deSweihe /// Difference for two sample units in a matched function according to the
1365540489deSweihe /// given match status.
1366540489deSweihe void updateOverlapStatsForFunction(uint64_t BaseSample, uint64_t TestSample,
1367540489deSweihe uint64_t HotBlockCount,
1368540489deSweihe SampleOverlapStats &FuncOverlap,
1369540489deSweihe double &Difference, MatchStatus Status);
1370540489deSweihe
1371540489deSweihe /// This function updates statistics in FuncOverlap, HotBlockOverlap, and
1372540489deSweihe /// Difference for unmatched callees that only present in one profile in a
1373540489deSweihe /// matched caller function.
1374540489deSweihe void updateForUnmatchedCallee(const sampleprof::FunctionSamples &Func,
1375540489deSweihe SampleOverlapStats &FuncOverlap,
1376540489deSweihe double &Difference, MatchStatus Status);
1377540489deSweihe
1378540489deSweihe /// This function updates sample overlap statistics of an overlap function in
1379540489deSweihe /// base and test profile. It also calculates a function-internal similarity
1380540489deSweihe /// FIS as follows:
1381540489deSweihe /// For offsets i that have samples in at least one profile in this
1382540489deSweihe /// function A, given BS(i) returned by computeBlockSimilarity(), compute
1383540489deSweihe /// FIS(A) = (2.0 - sum_i(1.0 - BS(i))) / 2, ranging in [0.0f to 1.0f] with
1384540489deSweihe /// 0.0 meaning no overlap.
1385540489deSweihe double computeSampleFunctionInternalOverlap(
1386540489deSweihe const sampleprof::FunctionSamples &BaseFunc,
1387540489deSweihe const sampleprof::FunctionSamples &TestFunc,
1388540489deSweihe SampleOverlapStats &FuncOverlap);
1389540489deSweihe
1390540489deSweihe /// Function-level similarity (FS) is a weighted value over function internal
1391540489deSweihe /// similarity (FIS). This function computes a function's FS from its FIS by
1392540489deSweihe /// applying the weight.
1393540489deSweihe double weightForFuncSimilarity(double FuncSimilarity, uint64_t BaseFuncSample,
1394540489deSweihe uint64_t TestFuncSample) const;
1395540489deSweihe
1396540489deSweihe /// The function-level similarity FS(A) for a function A is computed as
1397540489deSweihe /// follows:
1398540489deSweihe /// Compute a function-internal similarity FIS(A) by
1399540489deSweihe /// computeSampleFunctionInternalOverlap(). Then, with the weight of
1400540489deSweihe /// function A in base profile WB(A), and the weight of function A in test
1401540489deSweihe /// profile WT(A), compute FS(A) = FIS(A) * (1.0 - fabs(WB(A) - WT(A)))
1402540489deSweihe /// ranging in [0.0f to 1.0f] with 0.0 meaning no overlap.
1403540489deSweihe double
1404540489deSweihe computeSampleFunctionOverlap(const sampleprof::FunctionSamples *BaseFunc,
1405540489deSweihe const sampleprof::FunctionSamples *TestFunc,
1406540489deSweihe SampleOverlapStats *FuncOverlap,
1407540489deSweihe uint64_t BaseFuncSample,
1408540489deSweihe uint64_t TestFuncSample);
1409540489deSweihe
1410540489deSweihe /// Profile-level similarity (PS) is a weighted aggregate over function-level
1411540489deSweihe /// similarities (FS). This method weights the FS value by the function
1412540489deSweihe /// weights in the base and test profiles for the aggregation.
1413540489deSweihe double weightByImportance(double FuncSimilarity, uint64_t BaseFuncSample,
1414540489deSweihe uint64_t TestFuncSample) const;
1415540489deSweihe };
1416540489deSweihe } // end anonymous namespace
1417540489deSweihe
detectZeroSampleProfile(raw_fd_ostream & OS) const1418540489deSweihe bool SampleOverlapAggregator::detectZeroSampleProfile(
1419540489deSweihe raw_fd_ostream &OS) const {
1420540489deSweihe bool HaveZeroSample = false;
1421540489deSweihe if (ProfOverlap.BaseSample == 0) {
1422540489deSweihe OS << "Sum of sample counts for profile " << BaseFilename << " is 0.\n";
1423540489deSweihe HaveZeroSample = true;
1424540489deSweihe }
1425540489deSweihe if (ProfOverlap.TestSample == 0) {
1426540489deSweihe OS << "Sum of sample counts for profile " << TestFilename << " is 0.\n";
1427540489deSweihe HaveZeroSample = true;
1428540489deSweihe }
1429540489deSweihe return HaveZeroSample;
1430540489deSweihe }
1431540489deSweihe
computeBlockSimilarity(uint64_t BaseSample,uint64_t TestSample,const SampleOverlapStats & FuncOverlap) const1432540489deSweihe double SampleOverlapAggregator::computeBlockSimilarity(
1433540489deSweihe uint64_t BaseSample, uint64_t TestSample,
1434540489deSweihe const SampleOverlapStats &FuncOverlap) const {
1435540489deSweihe double BaseFrac = 0.0;
1436540489deSweihe double TestFrac = 0.0;
1437540489deSweihe if (FuncOverlap.BaseSample > 0)
1438540489deSweihe BaseFrac = static_cast<double>(BaseSample) / FuncOverlap.BaseSample;
1439540489deSweihe if (FuncOverlap.TestSample > 0)
1440540489deSweihe TestFrac = static_cast<double>(TestSample) / FuncOverlap.TestSample;
1441540489deSweihe return 1.0 - std::fabs(BaseFrac - TestFrac);
1442540489deSweihe }
1443540489deSweihe
updateHotBlockOverlap(uint64_t BaseSample,uint64_t TestSample,uint64_t HotBlockCount)1444540489deSweihe void SampleOverlapAggregator::updateHotBlockOverlap(uint64_t BaseSample,
1445540489deSweihe uint64_t TestSample,
1446540489deSweihe uint64_t HotBlockCount) {
1447540489deSweihe bool IsBaseHot = (BaseSample >= BaseHotThreshold);
1448540489deSweihe bool IsTestHot = (TestSample >= TestHotThreshold);
1449540489deSweihe if (!IsBaseHot && !IsTestHot)
1450540489deSweihe return;
1451540489deSweihe
1452540489deSweihe HotBlockOverlap.UnionCount += HotBlockCount;
1453540489deSweihe if (IsBaseHot)
1454540489deSweihe HotBlockOverlap.BaseCount += HotBlockCount;
1455540489deSweihe if (IsTestHot)
1456540489deSweihe HotBlockOverlap.TestCount += HotBlockCount;
1457540489deSweihe if (IsBaseHot && IsTestHot)
1458540489deSweihe HotBlockOverlap.OverlapCount += HotBlockCount;
1459540489deSweihe }
1460540489deSweihe
getHotFunctions(const FuncSampleStatsMap & ProfStats,FuncSampleStatsMap & HotFunc,uint64_t HotThreshold) const1461540489deSweihe void SampleOverlapAggregator::getHotFunctions(
1462b9db7036SHongtao Yu const FuncSampleStatsMap &ProfStats, FuncSampleStatsMap &HotFunc,
1463b9db7036SHongtao Yu uint64_t HotThreshold) const {
1464540489deSweihe for (const auto &F : ProfStats) {
1465540489deSweihe if (isFunctionHot(F.second, HotThreshold))
1466b9db7036SHongtao Yu HotFunc.emplace(F.first, F.second);
1467540489deSweihe }
1468540489deSweihe }
1469540489deSweihe
computeHotFuncOverlap()1470540489deSweihe void SampleOverlapAggregator::computeHotFuncOverlap() {
1471b9db7036SHongtao Yu FuncSampleStatsMap BaseHotFunc;
1472540489deSweihe getHotFunctions(BaseStats, BaseHotFunc, BaseHotThreshold);
1473540489deSweihe HotFuncOverlap.BaseCount = BaseHotFunc.size();
1474540489deSweihe
1475b9db7036SHongtao Yu FuncSampleStatsMap TestHotFunc;
1476540489deSweihe getHotFunctions(TestStats, TestHotFunc, TestHotThreshold);
1477540489deSweihe HotFuncOverlap.TestCount = TestHotFunc.size();
1478540489deSweihe HotFuncOverlap.UnionCount = HotFuncOverlap.TestCount;
1479540489deSweihe
1480540489deSweihe for (const auto &F : BaseHotFunc) {
1481b9db7036SHongtao Yu if (TestHotFunc.count(F.first))
1482540489deSweihe ++HotFuncOverlap.OverlapCount;
1483540489deSweihe else
1484540489deSweihe ++HotFuncOverlap.UnionCount;
1485540489deSweihe }
1486540489deSweihe }
1487540489deSweihe
updateOverlapStatsForFunction(uint64_t BaseSample,uint64_t TestSample,uint64_t HotBlockCount,SampleOverlapStats & FuncOverlap,double & Difference,MatchStatus Status)1488540489deSweihe void SampleOverlapAggregator::updateOverlapStatsForFunction(
1489540489deSweihe uint64_t BaseSample, uint64_t TestSample, uint64_t HotBlockCount,
1490540489deSweihe SampleOverlapStats &FuncOverlap, double &Difference, MatchStatus Status) {
1491540489deSweihe assert(Status != MS_None &&
1492540489deSweihe "Match status should be updated before updating overlap statistics");
1493540489deSweihe if (Status == MS_FirstUnique) {
1494540489deSweihe TestSample = 0;
1495540489deSweihe FuncOverlap.BaseUniqueSample += BaseSample;
1496540489deSweihe } else if (Status == MS_SecondUnique) {
1497540489deSweihe BaseSample = 0;
1498540489deSweihe FuncOverlap.TestUniqueSample += TestSample;
1499540489deSweihe } else {
1500540489deSweihe ++FuncOverlap.OverlapCount;
1501540489deSweihe }
1502540489deSweihe
1503540489deSweihe FuncOverlap.UnionSample += std::max(BaseSample, TestSample);
1504540489deSweihe FuncOverlap.OverlapSample += std::min(BaseSample, TestSample);
1505540489deSweihe Difference +=
1506540489deSweihe 1.0 - computeBlockSimilarity(BaseSample, TestSample, FuncOverlap);
1507540489deSweihe updateHotBlockOverlap(BaseSample, TestSample, HotBlockCount);
1508540489deSweihe }
1509540489deSweihe
updateForUnmatchedCallee(const sampleprof::FunctionSamples & Func,SampleOverlapStats & FuncOverlap,double & Difference,MatchStatus Status)1510540489deSweihe void SampleOverlapAggregator::updateForUnmatchedCallee(
1511540489deSweihe const sampleprof::FunctionSamples &Func, SampleOverlapStats &FuncOverlap,
1512540489deSweihe double &Difference, MatchStatus Status) {
1513540489deSweihe assert((Status == MS_FirstUnique || Status == MS_SecondUnique) &&
1514540489deSweihe "Status must be either of the two unmatched cases");
1515540489deSweihe FuncSampleStats FuncStats;
1516540489deSweihe if (Status == MS_FirstUnique) {
1517540489deSweihe getFuncSampleStats(Func, FuncStats, BaseHotThreshold);
1518540489deSweihe updateOverlapStatsForFunction(FuncStats.SampleSum, 0,
1519540489deSweihe FuncStats.HotBlockCount, FuncOverlap,
1520540489deSweihe Difference, Status);
1521540489deSweihe } else {
1522540489deSweihe getFuncSampleStats(Func, FuncStats, TestHotThreshold);
1523540489deSweihe updateOverlapStatsForFunction(0, FuncStats.SampleSum,
1524540489deSweihe FuncStats.HotBlockCount, FuncOverlap,
1525540489deSweihe Difference, Status);
1526540489deSweihe }
1527540489deSweihe }
1528540489deSweihe
computeSampleFunctionInternalOverlap(const sampleprof::FunctionSamples & BaseFunc,const sampleprof::FunctionSamples & TestFunc,SampleOverlapStats & FuncOverlap)1529540489deSweihe double SampleOverlapAggregator::computeSampleFunctionInternalOverlap(
1530540489deSweihe const sampleprof::FunctionSamples &BaseFunc,
1531540489deSweihe const sampleprof::FunctionSamples &TestFunc,
1532540489deSweihe SampleOverlapStats &FuncOverlap) {
1533540489deSweihe
1534540489deSweihe using namespace sampleprof;
1535540489deSweihe
1536540489deSweihe double Difference = 0;
1537540489deSweihe
1538540489deSweihe // Accumulate Difference for regular line/block samples in the function.
1539540489deSweihe // We match them through sort-merge join algorithm because
1540540489deSweihe // FunctionSamples::getBodySamples() returns a map of sample counters ordered
1541540489deSweihe // by their offsets.
1542540489deSweihe MatchStep<BodySampleMap::const_iterator> BlockIterStep(
1543540489deSweihe BaseFunc.getBodySamples().cbegin(), BaseFunc.getBodySamples().cend(),
1544540489deSweihe TestFunc.getBodySamples().cbegin(), TestFunc.getBodySamples().cend());
1545540489deSweihe BlockIterStep.updateOneStep();
1546540489deSweihe while (!BlockIterStep.areBothFinished()) {
1547540489deSweihe uint64_t BaseSample =
1548540489deSweihe BlockIterStep.isFirstFinished()
1549540489deSweihe ? 0
1550540489deSweihe : BlockIterStep.getFirstIter()->second.getSamples();
1551540489deSweihe uint64_t TestSample =
1552540489deSweihe BlockIterStep.isSecondFinished()
1553540489deSweihe ? 0
1554540489deSweihe : BlockIterStep.getSecondIter()->second.getSamples();
1555540489deSweihe updateOverlapStatsForFunction(BaseSample, TestSample, 1, FuncOverlap,
1556540489deSweihe Difference, BlockIterStep.getMatchStatus());
1557540489deSweihe
1558540489deSweihe BlockIterStep.updateOneStep();
1559540489deSweihe }
1560540489deSweihe
1561540489deSweihe // Accumulate Difference for callsite lines in the function. We match
1562540489deSweihe // them through sort-merge algorithm because
1563540489deSweihe // FunctionSamples::getCallsiteSamples() returns a map of callsite records
1564540489deSweihe // ordered by their offsets.
1565540489deSweihe MatchStep<CallsiteSampleMap::const_iterator> CallsiteIterStep(
1566540489deSweihe BaseFunc.getCallsiteSamples().cbegin(),
1567540489deSweihe BaseFunc.getCallsiteSamples().cend(),
1568540489deSweihe TestFunc.getCallsiteSamples().cbegin(),
1569540489deSweihe TestFunc.getCallsiteSamples().cend());
1570540489deSweihe CallsiteIterStep.updateOneStep();
1571540489deSweihe while (!CallsiteIterStep.areBothFinished()) {
1572540489deSweihe MatchStatus CallsiteStepStatus = CallsiteIterStep.getMatchStatus();
1573540489deSweihe assert(CallsiteStepStatus != MS_None &&
1574540489deSweihe "Match status should be updated before entering loop body");
1575540489deSweihe
1576540489deSweihe if (CallsiteStepStatus != MS_Match) {
1577540489deSweihe auto Callsite = (CallsiteStepStatus == MS_FirstUnique)
1578540489deSweihe ? CallsiteIterStep.getFirstIter()
1579540489deSweihe : CallsiteIterStep.getSecondIter();
1580540489deSweihe for (const auto &F : Callsite->second)
1581540489deSweihe updateForUnmatchedCallee(F.second, FuncOverlap, Difference,
1582540489deSweihe CallsiteStepStatus);
1583540489deSweihe } else {
1584540489deSweihe // There may be multiple inlinees at the same offset, so we need to try
1585540489deSweihe // matching all of them. This match is implemented through sort-merge
1586540489deSweihe // algorithm because callsite records at the same offset are ordered by
1587540489deSweihe // function names.
1588540489deSweihe MatchStep<FunctionSamplesMap::const_iterator> CalleeIterStep(
1589540489deSweihe CallsiteIterStep.getFirstIter()->second.cbegin(),
1590540489deSweihe CallsiteIterStep.getFirstIter()->second.cend(),
1591540489deSweihe CallsiteIterStep.getSecondIter()->second.cbegin(),
1592540489deSweihe CallsiteIterStep.getSecondIter()->second.cend());
1593540489deSweihe CalleeIterStep.updateOneStep();
1594540489deSweihe while (!CalleeIterStep.areBothFinished()) {
1595540489deSweihe MatchStatus CalleeStepStatus = CalleeIterStep.getMatchStatus();
1596540489deSweihe if (CalleeStepStatus != MS_Match) {
1597540489deSweihe auto Callee = (CalleeStepStatus == MS_FirstUnique)
1598540489deSweihe ? CalleeIterStep.getFirstIter()
1599540489deSweihe : CalleeIterStep.getSecondIter();
1600540489deSweihe updateForUnmatchedCallee(Callee->second, FuncOverlap, Difference,
1601540489deSweihe CalleeStepStatus);
1602540489deSweihe } else {
1603540489deSweihe // An inlined function can contain other inlinees inside, so compute
1604540489deSweihe // the Difference recursively.
1605540489deSweihe Difference += 2.0 - 2 * computeSampleFunctionInternalOverlap(
1606540489deSweihe CalleeIterStep.getFirstIter()->second,
1607540489deSweihe CalleeIterStep.getSecondIter()->second,
1608540489deSweihe FuncOverlap);
1609540489deSweihe }
1610540489deSweihe CalleeIterStep.updateOneStep();
1611540489deSweihe }
1612540489deSweihe }
1613540489deSweihe CallsiteIterStep.updateOneStep();
1614540489deSweihe }
1615540489deSweihe
1616540489deSweihe // Difference reflects the total differences of line/block samples in this
1617540489deSweihe // function and ranges in [0.0f to 2.0f]. Take (2.0 - Difference) / 2 to
1618540489deSweihe // reflect the similarity between function profiles in [0.0f to 1.0f].
1619540489deSweihe return (2.0 - Difference) / 2;
1620540489deSweihe }
1621540489deSweihe
weightForFuncSimilarity(double FuncInternalSimilarity,uint64_t BaseFuncSample,uint64_t TestFuncSample) const1622540489deSweihe double SampleOverlapAggregator::weightForFuncSimilarity(
1623540489deSweihe double FuncInternalSimilarity, uint64_t BaseFuncSample,
1624540489deSweihe uint64_t TestFuncSample) const {
1625540489deSweihe // Compute the weight as the distance between the function weights in two
1626540489deSweihe // profiles.
1627540489deSweihe double BaseFrac = 0.0;
1628540489deSweihe double TestFrac = 0.0;
1629540489deSweihe assert(ProfOverlap.BaseSample > 0 &&
1630540489deSweihe "Total samples in base profile should be greater than 0");
1631540489deSweihe BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample;
1632540489deSweihe assert(ProfOverlap.TestSample > 0 &&
1633540489deSweihe "Total samples in test profile should be greater than 0");
1634540489deSweihe TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample;
1635540489deSweihe double WeightDistance = std::fabs(BaseFrac - TestFrac);
1636540489deSweihe
1637540489deSweihe // Take WeightDistance into the similarity.
1638540489deSweihe return FuncInternalSimilarity * (1 - WeightDistance);
1639540489deSweihe }
1640540489deSweihe
1641540489deSweihe double
weightByImportance(double FuncSimilarity,uint64_t BaseFuncSample,uint64_t TestFuncSample) const1642540489deSweihe SampleOverlapAggregator::weightByImportance(double FuncSimilarity,
1643540489deSweihe uint64_t BaseFuncSample,
1644540489deSweihe uint64_t TestFuncSample) const {
1645540489deSweihe
1646540489deSweihe double BaseFrac = 0.0;
1647540489deSweihe double TestFrac = 0.0;
1648540489deSweihe assert(ProfOverlap.BaseSample > 0 &&
1649540489deSweihe "Total samples in base profile should be greater than 0");
1650540489deSweihe BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample / 2.0;
1651540489deSweihe assert(ProfOverlap.TestSample > 0 &&
1652540489deSweihe "Total samples in test profile should be greater than 0");
1653540489deSweihe TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample / 2.0;
1654540489deSweihe return FuncSimilarity * (BaseFrac + TestFrac);
1655540489deSweihe }
1656540489deSweihe
computeSampleFunctionOverlap(const sampleprof::FunctionSamples * BaseFunc,const sampleprof::FunctionSamples * TestFunc,SampleOverlapStats * FuncOverlap,uint64_t BaseFuncSample,uint64_t TestFuncSample)1657540489deSweihe double SampleOverlapAggregator::computeSampleFunctionOverlap(
1658540489deSweihe const sampleprof::FunctionSamples *BaseFunc,
1659540489deSweihe const sampleprof::FunctionSamples *TestFunc,
1660540489deSweihe SampleOverlapStats *FuncOverlap, uint64_t BaseFuncSample,
1661540489deSweihe uint64_t TestFuncSample) {
1662540489deSweihe // Default function internal similarity before weighted, meaning two functions
1663540489deSweihe // has no overlap.
1664540489deSweihe const double DefaultFuncInternalSimilarity = 0;
1665540489deSweihe double FuncSimilarity;
1666540489deSweihe double FuncInternalSimilarity;
1667540489deSweihe
1668540489deSweihe // If BaseFunc or TestFunc is nullptr, it means the functions do not overlap.
1669540489deSweihe // In this case, we use DefaultFuncInternalSimilarity as the function internal
1670540489deSweihe // similarity.
1671540489deSweihe if (!BaseFunc || !TestFunc) {
1672540489deSweihe FuncInternalSimilarity = DefaultFuncInternalSimilarity;
1673540489deSweihe } else {
1674540489deSweihe assert(FuncOverlap != nullptr &&
1675540489deSweihe "FuncOverlap should be provided in this case");
1676540489deSweihe FuncInternalSimilarity = computeSampleFunctionInternalOverlap(
1677540489deSweihe *BaseFunc, *TestFunc, *FuncOverlap);
1678540489deSweihe // Now, FuncInternalSimilarity may be a little less than 0 due to
1679540489deSweihe // imprecision of floating point accumulations. Make it zero if the
1680540489deSweihe // difference is below Epsilon.
1681540489deSweihe FuncInternalSimilarity = (std::fabs(FuncInternalSimilarity - 0) < Epsilon)
1682540489deSweihe ? 0
1683540489deSweihe : FuncInternalSimilarity;
1684540489deSweihe }
1685540489deSweihe FuncSimilarity = weightForFuncSimilarity(FuncInternalSimilarity,
1686540489deSweihe BaseFuncSample, TestFuncSample);
1687540489deSweihe return FuncSimilarity;
1688540489deSweihe }
1689540489deSweihe
computeSampleProfileOverlap(raw_fd_ostream & OS)1690540489deSweihe void SampleOverlapAggregator::computeSampleProfileOverlap(raw_fd_ostream &OS) {
1691540489deSweihe using namespace sampleprof;
1692540489deSweihe
1693b9db7036SHongtao Yu std::unordered_map<SampleContext, const FunctionSamples *,
1694b9db7036SHongtao Yu SampleContext::Hash>
1695b9db7036SHongtao Yu BaseFuncProf;
1696540489deSweihe const auto &BaseProfiles = BaseReader->getProfiles();
1697540489deSweihe for (const auto &BaseFunc : BaseProfiles) {
1698b9db7036SHongtao Yu BaseFuncProf.emplace(BaseFunc.second.getContext(), &(BaseFunc.second));
1699540489deSweihe }
1700540489deSweihe ProfOverlap.UnionCount = BaseFuncProf.size();
1701540489deSweihe
1702540489deSweihe const auto &TestProfiles = TestReader->getProfiles();
1703540489deSweihe for (const auto &TestFunc : TestProfiles) {
1704540489deSweihe SampleOverlapStats FuncOverlap;
1705b9db7036SHongtao Yu FuncOverlap.TestName = TestFunc.second.getContext();
1706540489deSweihe assert(TestStats.count(FuncOverlap.TestName) &&
1707540489deSweihe "TestStats should have records for all functions in test profile "
1708540489deSweihe "except inlinees");
1709540489deSweihe FuncOverlap.TestSample = TestStats[FuncOverlap.TestName].SampleSum;
1710540489deSweihe
17114ef88031SWenlei He bool Matched = false;
1712540489deSweihe const auto Match = BaseFuncProf.find(FuncOverlap.TestName);
1713540489deSweihe if (Match == BaseFuncProf.end()) {
1714540489deSweihe const FuncSampleStats &FuncStats = TestStats[FuncOverlap.TestName];
1715540489deSweihe ++ProfOverlap.TestUniqueCount;
1716540489deSweihe ProfOverlap.TestUniqueSample += FuncStats.SampleSum;
1717540489deSweihe FuncOverlap.TestUniqueSample = FuncStats.SampleSum;
1718540489deSweihe
1719540489deSweihe updateHotBlockOverlap(0, FuncStats.SampleSum, FuncStats.HotBlockCount);
1720540489deSweihe
1721540489deSweihe double FuncSimilarity = computeSampleFunctionOverlap(
1722540489deSweihe nullptr, nullptr, nullptr, 0, FuncStats.SampleSum);
1723540489deSweihe ProfOverlap.Similarity +=
1724540489deSweihe weightByImportance(FuncSimilarity, 0, FuncStats.SampleSum);
1725540489deSweihe
1726540489deSweihe ++ProfOverlap.UnionCount;
1727540489deSweihe ProfOverlap.UnionSample += FuncStats.SampleSum;
1728540489deSweihe } else {
1729540489deSweihe ++ProfOverlap.OverlapCount;
1730540489deSweihe
1731540489deSweihe // Two functions match with each other. Compute function-level overlap and
1732540489deSweihe // aggregate them into profile-level overlap.
1733b9db7036SHongtao Yu FuncOverlap.BaseName = Match->second->getContext();
1734540489deSweihe assert(BaseStats.count(FuncOverlap.BaseName) &&
1735540489deSweihe "BaseStats should have records for all functions in base profile "
1736540489deSweihe "except inlinees");
1737540489deSweihe FuncOverlap.BaseSample = BaseStats[FuncOverlap.BaseName].SampleSum;
1738540489deSweihe
1739540489deSweihe FuncOverlap.Similarity = computeSampleFunctionOverlap(
1740540489deSweihe Match->second, &TestFunc.second, &FuncOverlap, FuncOverlap.BaseSample,
1741540489deSweihe FuncOverlap.TestSample);
1742540489deSweihe ProfOverlap.Similarity +=
1743540489deSweihe weightByImportance(FuncOverlap.Similarity, FuncOverlap.BaseSample,
1744540489deSweihe FuncOverlap.TestSample);
1745540489deSweihe ProfOverlap.OverlapSample += FuncOverlap.OverlapSample;
1746540489deSweihe ProfOverlap.UnionSample += FuncOverlap.UnionSample;
1747540489deSweihe
1748540489deSweihe // Accumulate the percentage of base unique and test unique samples into
1749540489deSweihe // ProfOverlap.
1750540489deSweihe ProfOverlap.BaseUniqueSample += FuncOverlap.BaseUniqueSample;
1751540489deSweihe ProfOverlap.TestUniqueSample += FuncOverlap.TestUniqueSample;
1752540489deSweihe
1753540489deSweihe // Remove matched base functions for later reporting functions not found
1754540489deSweihe // in test profile.
1755540489deSweihe BaseFuncProf.erase(Match);
17564ef88031SWenlei He Matched = true;
1757540489deSweihe }
1758540489deSweihe
1759540489deSweihe // Print function-level similarity information if specified by options.
1760540489deSweihe assert(TestStats.count(FuncOverlap.TestName) &&
1761540489deSweihe "TestStats should have records for all functions in test profile "
1762540489deSweihe "except inlinees");
1763540489deSweihe if (TestStats[FuncOverlap.TestName].MaxSample >= FuncFilter.ValueCutoff ||
17644ef88031SWenlei He (Matched && FuncOverlap.Similarity < LowSimilarityThreshold) ||
17654ef88031SWenlei He (Matched && !FuncFilter.NameFilter.empty() &&
1766b9db7036SHongtao Yu FuncOverlap.BaseName.toString().find(FuncFilter.NameFilter) !=
1767b9db7036SHongtao Yu std::string::npos)) {
1768540489deSweihe assert(ProfOverlap.BaseSample > 0 &&
1769540489deSweihe "Total samples in base profile should be greater than 0");
1770540489deSweihe FuncOverlap.BaseWeight =
1771540489deSweihe static_cast<double>(FuncOverlap.BaseSample) / ProfOverlap.BaseSample;
1772540489deSweihe assert(ProfOverlap.TestSample > 0 &&
1773540489deSweihe "Total samples in test profile should be greater than 0");
1774540489deSweihe FuncOverlap.TestWeight =
1775540489deSweihe static_cast<double>(FuncOverlap.TestSample) / ProfOverlap.TestSample;
1776540489deSweihe FuncSimilarityDump.emplace(FuncOverlap.BaseWeight, FuncOverlap);
1777540489deSweihe }
1778540489deSweihe }
1779540489deSweihe
1780540489deSweihe // Traverse through functions in base profile but not in test profile.
1781540489deSweihe for (const auto &F : BaseFuncProf) {
1782b9db7036SHongtao Yu assert(BaseStats.count(F.second->getContext()) &&
1783540489deSweihe "BaseStats should have records for all functions in base profile "
1784540489deSweihe "except inlinees");
1785b9db7036SHongtao Yu const FuncSampleStats &FuncStats = BaseStats[F.second->getContext()];
1786540489deSweihe ++ProfOverlap.BaseUniqueCount;
1787540489deSweihe ProfOverlap.BaseUniqueSample += FuncStats.SampleSum;
1788540489deSweihe
1789540489deSweihe updateHotBlockOverlap(FuncStats.SampleSum, 0, FuncStats.HotBlockCount);
1790540489deSweihe
1791540489deSweihe double FuncSimilarity = computeSampleFunctionOverlap(
1792540489deSweihe nullptr, nullptr, nullptr, FuncStats.SampleSum, 0);
1793540489deSweihe ProfOverlap.Similarity +=
1794540489deSweihe weightByImportance(FuncSimilarity, FuncStats.SampleSum, 0);
1795540489deSweihe
1796540489deSweihe ProfOverlap.UnionSample += FuncStats.SampleSum;
1797540489deSweihe }
1798540489deSweihe
1799540489deSweihe // Now, ProfSimilarity may be a little greater than 1 due to imprecision
1800540489deSweihe // of floating point accumulations. Make it 1.0 if the difference is below
1801540489deSweihe // Epsilon.
1802540489deSweihe ProfOverlap.Similarity = (std::fabs(ProfOverlap.Similarity - 1) < Epsilon)
1803540489deSweihe ? 1
1804540489deSweihe : ProfOverlap.Similarity;
1805540489deSweihe
1806540489deSweihe computeHotFuncOverlap();
1807540489deSweihe }
1808540489deSweihe
initializeSampleProfileOverlap()1809540489deSweihe void SampleOverlapAggregator::initializeSampleProfileOverlap() {
1810540489deSweihe const auto &BaseProf = BaseReader->getProfiles();
1811540489deSweihe for (const auto &I : BaseProf) {
1812540489deSweihe ++ProfOverlap.BaseCount;
1813540489deSweihe FuncSampleStats FuncStats;
1814540489deSweihe getFuncSampleStats(I.second, FuncStats, BaseHotThreshold);
1815540489deSweihe ProfOverlap.BaseSample += FuncStats.SampleSum;
1816b9db7036SHongtao Yu BaseStats.emplace(I.second.getContext(), FuncStats);
1817540489deSweihe }
1818540489deSweihe
1819540489deSweihe const auto &TestProf = TestReader->getProfiles();
1820540489deSweihe for (const auto &I : TestProf) {
1821540489deSweihe ++ProfOverlap.TestCount;
1822540489deSweihe FuncSampleStats FuncStats;
1823540489deSweihe getFuncSampleStats(I.second, FuncStats, TestHotThreshold);
1824540489deSweihe ProfOverlap.TestSample += FuncStats.SampleSum;
1825b9db7036SHongtao Yu TestStats.emplace(I.second.getContext(), FuncStats);
1826540489deSweihe }
1827540489deSweihe
1828540489deSweihe ProfOverlap.BaseName = StringRef(BaseFilename);
1829540489deSweihe ProfOverlap.TestName = StringRef(TestFilename);
1830540489deSweihe }
1831540489deSweihe
dumpFuncSimilarity(raw_fd_ostream & OS) const1832540489deSweihe void SampleOverlapAggregator::dumpFuncSimilarity(raw_fd_ostream &OS) const {
1833540489deSweihe using namespace sampleprof;
1834540489deSweihe
1835540489deSweihe if (FuncSimilarityDump.empty())
1836540489deSweihe return;
1837540489deSweihe
1838540489deSweihe formatted_raw_ostream FOS(OS);
1839540489deSweihe FOS << "Function-level details:\n";
1840540489deSweihe FOS << "Base weight";
1841540489deSweihe FOS.PadToColumn(TestWeightCol);
1842540489deSweihe FOS << "Test weight";
1843540489deSweihe FOS.PadToColumn(SimilarityCol);
1844540489deSweihe FOS << "Similarity";
1845540489deSweihe FOS.PadToColumn(OverlapCol);
1846540489deSweihe FOS << "Overlap";
1847540489deSweihe FOS.PadToColumn(BaseUniqueCol);
1848540489deSweihe FOS << "Base unique";
1849540489deSweihe FOS.PadToColumn(TestUniqueCol);
1850540489deSweihe FOS << "Test unique";
1851540489deSweihe FOS.PadToColumn(BaseSampleCol);
1852540489deSweihe FOS << "Base samples";
1853540489deSweihe FOS.PadToColumn(TestSampleCol);
1854540489deSweihe FOS << "Test samples";
1855540489deSweihe FOS.PadToColumn(FuncNameCol);
1856540489deSweihe FOS << "Function name\n";
1857540489deSweihe for (const auto &F : FuncSimilarityDump) {
1858540489deSweihe double OverlapPercent =
1859540489deSweihe F.second.UnionSample > 0
1860540489deSweihe ? static_cast<double>(F.second.OverlapSample) / F.second.UnionSample
1861540489deSweihe : 0;
1862540489deSweihe double BaseUniquePercent =
1863540489deSweihe F.second.BaseSample > 0
1864540489deSweihe ? static_cast<double>(F.second.BaseUniqueSample) /
1865540489deSweihe F.second.BaseSample
1866540489deSweihe : 0;
1867540489deSweihe double TestUniquePercent =
1868540489deSweihe F.second.TestSample > 0
1869540489deSweihe ? static_cast<double>(F.second.TestUniqueSample) /
1870540489deSweihe F.second.TestSample
1871540489deSweihe : 0;
1872540489deSweihe
1873540489deSweihe FOS << format("%.2f%%", F.second.BaseWeight * 100);
1874540489deSweihe FOS.PadToColumn(TestWeightCol);
1875540489deSweihe FOS << format("%.2f%%", F.second.TestWeight * 100);
1876540489deSweihe FOS.PadToColumn(SimilarityCol);
1877540489deSweihe FOS << format("%.2f%%", F.second.Similarity * 100);
1878540489deSweihe FOS.PadToColumn(OverlapCol);
1879540489deSweihe FOS << format("%.2f%%", OverlapPercent * 100);
1880540489deSweihe FOS.PadToColumn(BaseUniqueCol);
1881540489deSweihe FOS << format("%.2f%%", BaseUniquePercent * 100);
1882540489deSweihe FOS.PadToColumn(TestUniqueCol);
1883540489deSweihe FOS << format("%.2f%%", TestUniquePercent * 100);
1884540489deSweihe FOS.PadToColumn(BaseSampleCol);
1885540489deSweihe FOS << F.second.BaseSample;
1886540489deSweihe FOS.PadToColumn(TestSampleCol);
1887540489deSweihe FOS << F.second.TestSample;
1888540489deSweihe FOS.PadToColumn(FuncNameCol);
1889b9db7036SHongtao Yu FOS << F.second.TestName.toString() << "\n";
1890540489deSweihe }
1891540489deSweihe }
1892540489deSweihe
dumpProgramSummary(raw_fd_ostream & OS) const1893540489deSweihe void SampleOverlapAggregator::dumpProgramSummary(raw_fd_ostream &OS) const {
1894b9db7036SHongtao Yu OS << "Profile overlap infomation for base_profile: "
1895b9db7036SHongtao Yu << ProfOverlap.BaseName.toString()
1896b9db7036SHongtao Yu << " and test_profile: " << ProfOverlap.TestName.toString()
1897b9db7036SHongtao Yu << "\nProgram level:\n";
1898540489deSweihe
1899540489deSweihe OS << " Whole program profile similarity: "
1900540489deSweihe << format("%.3f%%", ProfOverlap.Similarity * 100) << "\n";
1901540489deSweihe
1902540489deSweihe assert(ProfOverlap.UnionSample > 0 &&
1903540489deSweihe "Total samples in two profile should be greater than 0");
1904540489deSweihe double OverlapPercent =
1905540489deSweihe static_cast<double>(ProfOverlap.OverlapSample) / ProfOverlap.UnionSample;
1906540489deSweihe assert(ProfOverlap.BaseSample > 0 &&
1907540489deSweihe "Total samples in base profile should be greater than 0");
1908540489deSweihe double BaseUniquePercent = static_cast<double>(ProfOverlap.BaseUniqueSample) /
1909540489deSweihe ProfOverlap.BaseSample;
1910540489deSweihe assert(ProfOverlap.TestSample > 0 &&
1911540489deSweihe "Total samples in test profile should be greater than 0");
1912540489deSweihe double TestUniquePercent = static_cast<double>(ProfOverlap.TestUniqueSample) /
1913540489deSweihe ProfOverlap.TestSample;
1914540489deSweihe
1915540489deSweihe OS << " Whole program sample overlap: "
1916540489deSweihe << format("%.3f%%", OverlapPercent * 100) << "\n";
1917540489deSweihe OS << " percentage of samples unique in base profile: "
1918540489deSweihe << format("%.3f%%", BaseUniquePercent * 100) << "\n";
1919540489deSweihe OS << " percentage of samples unique in test profile: "
1920540489deSweihe << format("%.3f%%", TestUniquePercent * 100) << "\n";
1921540489deSweihe OS << " total samples in base profile: " << ProfOverlap.BaseSample << "\n"
1922540489deSweihe << " total samples in test profile: " << ProfOverlap.TestSample << "\n";
1923540489deSweihe
1924540489deSweihe assert(ProfOverlap.UnionCount > 0 &&
1925540489deSweihe "There should be at least one function in two input profiles");
1926540489deSweihe double FuncOverlapPercent =
1927540489deSweihe static_cast<double>(ProfOverlap.OverlapCount) / ProfOverlap.UnionCount;
1928540489deSweihe OS << " Function overlap: " << format("%.3f%%", FuncOverlapPercent * 100)
1929540489deSweihe << "\n";
1930540489deSweihe OS << " overlap functions: " << ProfOverlap.OverlapCount << "\n";
1931540489deSweihe OS << " functions unique in base profile: " << ProfOverlap.BaseUniqueCount
1932540489deSweihe << "\n";
1933540489deSweihe OS << " functions unique in test profile: " << ProfOverlap.TestUniqueCount
1934540489deSweihe << "\n";
1935540489deSweihe }
1936540489deSweihe
dumpHotFuncAndBlockOverlap(raw_fd_ostream & OS) const1937540489deSweihe void SampleOverlapAggregator::dumpHotFuncAndBlockOverlap(
1938540489deSweihe raw_fd_ostream &OS) const {
1939540489deSweihe assert(HotFuncOverlap.UnionCount > 0 &&
1940540489deSweihe "There should be at least one hot function in two input profiles");
1941540489deSweihe OS << " Hot-function overlap: "
1942540489deSweihe << format("%.3f%%", static_cast<double>(HotFuncOverlap.OverlapCount) /
1943540489deSweihe HotFuncOverlap.UnionCount * 100)
1944540489deSweihe << "\n";
1945540489deSweihe OS << " overlap hot functions: " << HotFuncOverlap.OverlapCount << "\n";
1946540489deSweihe OS << " hot functions unique in base profile: "
1947540489deSweihe << HotFuncOverlap.BaseCount - HotFuncOverlap.OverlapCount << "\n";
1948540489deSweihe OS << " hot functions unique in test profile: "
1949540489deSweihe << HotFuncOverlap.TestCount - HotFuncOverlap.OverlapCount << "\n";
1950540489deSweihe
1951540489deSweihe assert(HotBlockOverlap.UnionCount > 0 &&
1952540489deSweihe "There should be at least one hot block in two input profiles");
1953540489deSweihe OS << " Hot-block overlap: "
1954540489deSweihe << format("%.3f%%", static_cast<double>(HotBlockOverlap.OverlapCount) /
1955540489deSweihe HotBlockOverlap.UnionCount * 100)
1956540489deSweihe << "\n";
1957540489deSweihe OS << " overlap hot blocks: " << HotBlockOverlap.OverlapCount << "\n";
1958540489deSweihe OS << " hot blocks unique in base profile: "
1959540489deSweihe << HotBlockOverlap.BaseCount - HotBlockOverlap.OverlapCount << "\n";
1960540489deSweihe OS << " hot blocks unique in test profile: "
1961540489deSweihe << HotBlockOverlap.TestCount - HotBlockOverlap.OverlapCount << "\n";
1962540489deSweihe }
1963540489deSweihe
loadProfiles()1964540489deSweihe std::error_code SampleOverlapAggregator::loadProfiles() {
1965540489deSweihe using namespace sampleprof;
1966540489deSweihe
1967540489deSweihe LLVMContext Context;
19688d581857SRong Xu auto BaseReaderOrErr = SampleProfileReader::create(BaseFilename, Context,
19698d581857SRong Xu FSDiscriminatorPassOption);
1970540489deSweihe if (std::error_code EC = BaseReaderOrErr.getError())
1971540489deSweihe exitWithErrorCode(EC, BaseFilename);
1972540489deSweihe
19738d581857SRong Xu auto TestReaderOrErr = SampleProfileReader::create(TestFilename, Context,
19748d581857SRong Xu FSDiscriminatorPassOption);
1975540489deSweihe if (std::error_code EC = TestReaderOrErr.getError())
1976540489deSweihe exitWithErrorCode(EC, TestFilename);
1977540489deSweihe
1978540489deSweihe BaseReader = std::move(BaseReaderOrErr.get());
1979540489deSweihe TestReader = std::move(TestReaderOrErr.get());
1980540489deSweihe
1981540489deSweihe if (std::error_code EC = BaseReader->read())
1982540489deSweihe exitWithErrorCode(EC, BaseFilename);
1983540489deSweihe if (std::error_code EC = TestReader->read())
1984540489deSweihe exitWithErrorCode(EC, TestFilename);
1985ac068e01SHongtao Yu if (BaseReader->profileIsProbeBased() != TestReader->profileIsProbeBased())
1986ac068e01SHongtao Yu exitWithError(
1987ac068e01SHongtao Yu "cannot compare probe-based profile with non-probe-based profile");
1988e36786d1SHongtao Yu if (BaseReader->profileIsCS() != TestReader->profileIsCS())
1989e68fafa4SHongtao Yu exitWithError("cannot compare CS profile with non-CS profile");
1990540489deSweihe
1991540489deSweihe // Load BaseHotThreshold and TestHotThreshold as 99-percentile threshold in
1992540489deSweihe // profile summary.
1993540489deSweihe ProfileSummary &BasePS = BaseReader->getSummary();
1994540489deSweihe ProfileSummary &TestPS = TestReader->getSummary();
19959978e0e4SWenlei He BaseHotThreshold =
19969978e0e4SWenlei He ProfileSummaryBuilder::getHotCountThreshold(BasePS.getDetailedSummary());
19979978e0e4SWenlei He TestHotThreshold =
19989978e0e4SWenlei He ProfileSummaryBuilder::getHotCountThreshold(TestPS.getDetailedSummary());
19999978e0e4SWenlei He
2000540489deSweihe return std::error_code();
2001540489deSweihe }
2002540489deSweihe
overlapSampleProfile(const std::string & BaseFilename,const std::string & TestFilename,const OverlapFuncFilters & FuncFilter,uint64_t SimilarityCutoff,raw_fd_ostream & OS)2003540489deSweihe void overlapSampleProfile(const std::string &BaseFilename,
2004540489deSweihe const std::string &TestFilename,
2005540489deSweihe const OverlapFuncFilters &FuncFilter,
2006540489deSweihe uint64_t SimilarityCutoff, raw_fd_ostream &OS) {
2007540489deSweihe using namespace sampleprof;
2008540489deSweihe
2009540489deSweihe // We use 0.000005 to initialize OverlapAggr.Epsilon because the final metrics
2010540489deSweihe // report 2--3 places after decimal point in percentage numbers.
2011540489deSweihe SampleOverlapAggregator OverlapAggr(
2012540489deSweihe BaseFilename, TestFilename,
2013540489deSweihe static_cast<double>(SimilarityCutoff) / 1000000, 0.000005, FuncFilter);
2014540489deSweihe if (std::error_code EC = OverlapAggr.loadProfiles())
2015540489deSweihe exitWithErrorCode(EC);
2016540489deSweihe
2017540489deSweihe OverlapAggr.initializeSampleProfileOverlap();
2018540489deSweihe if (OverlapAggr.detectZeroSampleProfile(OS))
2019540489deSweihe return;
2020540489deSweihe
2021540489deSweihe OverlapAggr.computeSampleProfileOverlap(OS);
2022540489deSweihe
2023540489deSweihe OverlapAggr.dumpProgramSummary(OS);
2024540489deSweihe OverlapAggr.dumpHotFuncAndBlockOverlap(OS);
2025540489deSweihe OverlapAggr.dumpFuncSimilarity(OS);
2026540489deSweihe }
2027540489deSweihe
overlap_main(int argc,const char * argv[])2028998b97f6SRong Xu static int overlap_main(int argc, const char *argv[]) {
2029998b97f6SRong Xu cl::opt<std::string> BaseFilename(cl::Positional, cl::Required,
2030998b97f6SRong Xu cl::desc("<base profile file>"));
2031998b97f6SRong Xu cl::opt<std::string> TestFilename(cl::Positional, cl::Required,
2032998b97f6SRong Xu cl::desc("<test profile file>"));
2033998b97f6SRong Xu cl::opt<std::string> Output("output", cl::value_desc("output"), cl::init("-"),
2034998b97f6SRong Xu cl::desc("Output file"));
2035998b97f6SRong Xu cl::alias OutputA("o", cl::desc("Alias for --output"), cl::aliasopt(Output));
2036e68fafa4SHongtao Yu cl::opt<bool> IsCS(
2037e68fafa4SHongtao Yu "cs", cl::init(false),
2038e68fafa4SHongtao Yu cl::desc("For context sensitive PGO counts. Does not work with CSSPGO."));
2039998b97f6SRong Xu cl::opt<unsigned long long> ValueCutoff(
2040998b97f6SRong Xu "value-cutoff", cl::init(-1),
2041998b97f6SRong Xu cl::desc(
2042e68fafa4SHongtao Yu "Function level overlap information for every function (with calling "
2043e68fafa4SHongtao Yu "context for csspgo) in test "
2044998b97f6SRong Xu "profile with max count value greater then the parameter value"));
2045998b97f6SRong Xu cl::opt<std::string> FuncNameFilter(
2046998b97f6SRong Xu "function",
2047e68fafa4SHongtao Yu cl::desc("Function level overlap information for matching functions. For "
2048e68fafa4SHongtao Yu "CSSPGO this takes a a function name with calling context"));
2049540489deSweihe cl::opt<unsigned long long> SimilarityCutoff(
2050540489deSweihe "similarity-cutoff", cl::init(0),
2051e68fafa4SHongtao Yu cl::desc("For sample profiles, list function names (with calling context "
2052e68fafa4SHongtao Yu "for csspgo) for overlapped functions "
2053540489deSweihe "with similarities below the cutoff (percentage times 10000)."));
2054540489deSweihe cl::opt<ProfileKinds> ProfileKind(
2055540489deSweihe cl::desc("Profile kind:"), cl::init(instr),
2056540489deSweihe cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
2057540489deSweihe clEnumVal(sample, "Sample profile")));
2058998b97f6SRong Xu cl::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n");
2059998b97f6SRong Xu
2060998b97f6SRong Xu std::error_code EC;
206182b3e28eSAbhina Sreeskantharajan raw_fd_ostream OS(Output.data(), EC, sys::fs::OF_TextWithCRLF);
2062998b97f6SRong Xu if (EC)
2063998b97f6SRong Xu exitWithErrorCode(EC, Output);
2064998b97f6SRong Xu
2065540489deSweihe if (ProfileKind == instr)
2066998b97f6SRong Xu overlapInstrProfile(BaseFilename, TestFilename,
2067998b97f6SRong Xu OverlapFuncFilters{ValueCutoff, FuncNameFilter}, OS,
2068998b97f6SRong Xu IsCS);
2069540489deSweihe else
2070540489deSweihe overlapSampleProfile(BaseFilename, TestFilename,
2071540489deSweihe OverlapFuncFilters{ValueCutoff, FuncNameFilter},
2072540489deSweihe SimilarityCutoff, OS);
2073998b97f6SRong Xu
2074998b97f6SRong Xu return 0;
2075998b97f6SRong Xu }
2076998b97f6SRong Xu
207759d90fe8SFangrui Song namespace {
207859d90fe8SFangrui Song struct ValueSitesStats {
ValueSitesStats__anon73ab2c7b0e11::ValueSitesStats20790cf1f56aSRong Xu ValueSitesStats()
20800cf1f56aSRong Xu : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0),
20810cf1f56aSRong Xu TotalNumValues(0) {}
20820cf1f56aSRong Xu uint64_t TotalNumValueSites;
20830cf1f56aSRong Xu uint64_t TotalNumValueSitesWithValueProfile;
20840cf1f56aSRong Xu uint64_t TotalNumValues;
20850cf1f56aSRong Xu std::vector<unsigned> ValueSitesHistogram;
208659d90fe8SFangrui Song };
208759d90fe8SFangrui Song } // namespace
20880cf1f56aSRong Xu
traverseAllValueSites(const InstrProfRecord & Func,uint32_t VK,ValueSitesStats & Stats,raw_fd_ostream & OS,InstrProfSymtab * Symtab)20890cf1f56aSRong Xu static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK,
20900cf1f56aSRong Xu ValueSitesStats &Stats, raw_fd_ostream &OS,
209160faea19SRong Xu InstrProfSymtab *Symtab) {
20920cf1f56aSRong Xu uint32_t NS = Func.getNumValueSites(VK);
20930cf1f56aSRong Xu Stats.TotalNumValueSites += NS;
20940cf1f56aSRong Xu for (size_t I = 0; I < NS; ++I) {
20950cf1f56aSRong Xu uint32_t NV = Func.getNumValueDataForSite(VK, I);
20960cf1f56aSRong Xu std::unique_ptr<InstrProfValueData[]> VD = Func.getValueForSite(VK, I);
20970cf1f56aSRong Xu Stats.TotalNumValues += NV;
20980cf1f56aSRong Xu if (NV) {
20990cf1f56aSRong Xu Stats.TotalNumValueSitesWithValueProfile++;
21000cf1f56aSRong Xu if (NV > Stats.ValueSitesHistogram.size())
21010cf1f56aSRong Xu Stats.ValueSitesHistogram.resize(NV, 0);
21020cf1f56aSRong Xu Stats.ValueSitesHistogram[NV - 1]++;
21030cf1f56aSRong Xu }
210452aa224aSRong Xu
210552aa224aSRong Xu uint64_t SiteSum = 0;
210652aa224aSRong Xu for (uint32_t V = 0; V < NV; V++)
210752aa224aSRong Xu SiteSum += VD[V].Count;
210852aa224aSRong Xu if (SiteSum == 0)
210952aa224aSRong Xu SiteSum = 1;
211052aa224aSRong Xu
21110cf1f56aSRong Xu for (uint32_t V = 0; V < NV; V++) {
211252aa224aSRong Xu OS << "\t[ " << format("%2u", I) << ", ";
211360faea19SRong Xu if (Symtab == nullptr)
211440a7f63cSPetar Jovanovic OS << format("%4" PRIu64, VD[V].Value);
211560faea19SRong Xu else
211660faea19SRong Xu OS << Symtab->getFuncName(VD[V].Value);
211752aa224aSRong Xu OS << ", " << format("%10" PRId64, VD[V].Count) << " ] ("
211852aa224aSRong Xu << format("%.2f%%", (VD[V].Count * 100.0 / SiteSum)) << ")\n";
21190cf1f56aSRong Xu }
21200cf1f56aSRong Xu }
21210cf1f56aSRong Xu }
21220cf1f56aSRong Xu
showValueSitesStats(raw_fd_ostream & OS,uint32_t VK,ValueSitesStats & Stats)21230cf1f56aSRong Xu static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK,
21240cf1f56aSRong Xu ValueSitesStats &Stats) {
21250cf1f56aSRong Xu OS << " Total number of sites: " << Stats.TotalNumValueSites << "\n";
21260cf1f56aSRong Xu OS << " Total number of sites with values: "
21270cf1f56aSRong Xu << Stats.TotalNumValueSitesWithValueProfile << "\n";
21280cf1f56aSRong Xu OS << " Total number of profiled values: " << Stats.TotalNumValues << "\n";
21290cf1f56aSRong Xu
21300cf1f56aSRong Xu OS << " Value sites histogram:\n\tNumTargets, SiteCount\n";
21310cf1f56aSRong Xu for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) {
21320cf1f56aSRong Xu if (Stats.ValueSitesHistogram[I] > 0)
21330cf1f56aSRong Xu OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n";
21340cf1f56aSRong Xu }
21350cf1f56aSRong Xu }
21360cf1f56aSRong Xu
showInstrProfile(const std::string & Filename,bool ShowCounts,uint32_t TopN,bool ShowIndirectCallTargets,bool ShowMemOPSizes,bool ShowDetailedSummary,std::vector<uint32_t> DetailedSummaryCutoffs,bool ShowAllFunctions,bool ShowCS,uint64_t ValueCutoff,bool OnlyListBelow,const std::string & ShowFunction,bool TextFormat,bool ShowBinaryIds,bool ShowCovered,raw_fd_ostream & OS)21371afc1de4SBenjamin Kramer static int showInstrProfile(const std::string &Filename, bool ShowCounts,
2138801b5319SXinliang David Li uint32_t TopN, bool ShowIndirectCallTargets,
2139801b5319SXinliang David Li bool ShowMemOPSizes, bool ShowDetailedSummary,
2140183ebbe0SEaswaran Raman std::vector<uint32_t> DetailedSummaryCutoffs,
2141a6ff69f6SRong Xu bool ShowAllFunctions, bool ShowCS,
2142a6ff69f6SRong Xu uint64_t ValueCutoff, bool OnlyListBelow,
2143a6ff69f6SRong Xu const std::string &ShowFunction, bool TextFormat,
214411d30742SEllis Hoag bool ShowBinaryIds, bool ShowCovered,
214511d30742SEllis Hoag raw_fd_ostream &OS) {
2146fcd55607SDiego Novillo auto ReaderOrErr = InstrProfReader::create(Filename);
21471afc1de4SBenjamin Kramer std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs);
21481afc1de4SBenjamin Kramer if (ShowDetailedSummary && Cutoffs.empty()) {
21490ca8ff4dSWenlei He Cutoffs = ProfileSummaryBuilder::DefaultCutoffs;
2150183ebbe0SEaswaran Raman }
21511afc1de4SBenjamin Kramer InstrProfSummaryBuilder Builder(std::move(Cutoffs));
21529152fd17SVedant Kumar if (Error E = ReaderOrErr.takeError())
21539152fd17SVedant Kumar exitWithError(std::move(E), Filename);
21549af28ef9SJustin Bogner
2155fcd55607SDiego Novillo auto Reader = std::move(ReaderOrErr.get());
215633c76c0cSRong Xu bool IsIRInstr = Reader->isIRLevelProfile();
2157183ebbe0SEaswaran Raman size_t ShownFunctions = 0;
215852aa224aSRong Xu size_t BelowCutoffFunctions = 0;
21590cf1f56aSRong Xu int NumVPKind = IPVK_Last - IPVK_First + 1;
21600cf1f56aSRong Xu std::vector<ValueSitesStats> VPStats(NumVPKind);
2161801b5319SXinliang David Li
2162801b5319SXinliang David Li auto MinCmp = [](const std::pair<std::string, uint64_t> &v1,
2163801b5319SXinliang David Li const std::pair<std::string, uint64_t> &v2) {
2164801b5319SXinliang David Li return v1.second > v2.second;
2165801b5319SXinliang David Li };
2166801b5319SXinliang David Li
2167801b5319SXinliang David Li std::priority_queue<std::pair<std::string, uint64_t>,
2168801b5319SXinliang David Li std::vector<std::pair<std::string, uint64_t>>,
2169801b5319SXinliang David Li decltype(MinCmp)>
2170801b5319SXinliang David Li HottestFuncs(MinCmp);
2171801b5319SXinliang David Li
217252aa224aSRong Xu if (!TextFormat && OnlyListBelow) {
217352aa224aSRong Xu OS << "The list of functions with the maximum counter less than "
217452aa224aSRong Xu << ValueCutoff << ":\n";
217552aa224aSRong Xu }
217652aa224aSRong Xu
2177c6ba9ca1SRichard Smith // Add marker so that IR-level instrumentation round-trips properly.
2178c6ba9ca1SRichard Smith if (TextFormat && IsIRInstr)
2179c6ba9ca1SRichard Smith OS << ":ir\n";
2180c6ba9ca1SRichard Smith
21819af28ef9SJustin Bogner for (const auto &Func : *Reader) {
2182a6ff69f6SRong Xu if (Reader->isIRLevelProfile()) {
2183a6ff69f6SRong Xu bool FuncIsCS = NamedInstrProfRecord::hasCSFlagInHash(Func.Hash);
2184a6ff69f6SRong Xu if (FuncIsCS != ShowCS)
2185a6ff69f6SRong Xu continue;
2186a6ff69f6SRong Xu }
21874e3eebc6SKazu Hirata bool Show = ShowAllFunctions ||
21884e3eebc6SKazu Hirata (!ShowFunction.empty() && Func.Name.contains(ShowFunction));
21899af28ef9SJustin Bogner
2190c6ba9ca1SRichard Smith bool doTextFormatDump = (Show && TextFormat);
21916f7c19a4SXinliang David Li
21926f7c19a4SXinliang David Li if (doTextFormatDump) {
2193a716cc5cSXinliang David Li InstrProfSymtab &Symtab = Reader->getSymtab();
2194cf9d52c6SDavid Blaikie InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab,
2195cf9d52c6SDavid Blaikie OS);
21966f7c19a4SXinliang David Li continue;
21976f7c19a4SXinliang David Li }
21986f7c19a4SXinliang David Li
2199b59d7c73SJustin Bogner assert(Func.Counts.size() > 0 && "function missing entry counter");
2200e5a17e3fSEaswaran Raman Builder.addRecord(Func);
22016f7c19a4SXinliang David Li
220211d30742SEllis Hoag if (ShowCovered) {
2203*9e88cbccSKazu Hirata if (llvm::any_of(Func.Counts, [](uint64_t C) { return C; }))
220411d30742SEllis Hoag OS << Func.Name << "\n";
220511d30742SEllis Hoag continue;
220611d30742SEllis Hoag }
220711d30742SEllis Hoag
22087162e16eSRong Xu uint64_t FuncMax = 0;
220952aa224aSRong Xu uint64_t FuncSum = 0;
221052aa224aSRong Xu for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) {
2211a23f6234SWei Mi if (Func.Counts[I] == (uint64_t)-1)
2212a23f6234SWei Mi continue;
22137162e16eSRong Xu FuncMax = std::max(FuncMax, Func.Counts[I]);
221452aa224aSRong Xu FuncSum += Func.Counts[I];
221552aa224aSRong Xu }
22167162e16eSRong Xu
221752aa224aSRong Xu if (FuncMax < ValueCutoff) {
221852aa224aSRong Xu ++BelowCutoffFunctions;
221952aa224aSRong Xu if (OnlyListBelow) {
222052aa224aSRong Xu OS << " " << Func.Name << ": (Max = " << FuncMax
222152aa224aSRong Xu << " Sum = " << FuncSum << ")\n";
222252aa224aSRong Xu }
222352aa224aSRong Xu continue;
222452aa224aSRong Xu } else if (OnlyListBelow)
222552aa224aSRong Xu continue;
222652aa224aSRong Xu
222752aa224aSRong Xu if (TopN) {
2228801b5319SXinliang David Li if (HottestFuncs.size() == TopN) {
2229801b5319SXinliang David Li if (HottestFuncs.top().second < FuncMax) {
2230801b5319SXinliang David Li HottestFuncs.pop();
2231801b5319SXinliang David Li HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
2232801b5319SXinliang David Li }
2233801b5319SXinliang David Li } else
2234801b5319SXinliang David Li HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
2235801b5319SXinliang David Li }
2236801b5319SXinliang David Li
22379af28ef9SJustin Bogner if (Show) {
22389af28ef9SJustin Bogner if (!ShownFunctions)
22399af28ef9SJustin Bogner OS << "Counters:\n";
22406f7c19a4SXinliang David Li
22419af28ef9SJustin Bogner ++ShownFunctions;
22429af28ef9SJustin Bogner
22439af28ef9SJustin Bogner OS << " " << Func.Name << ":\n"
2244423380f9SJustin Bogner << " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n"
224533c76c0cSRong Xu << " Counters: " << Func.Counts.size() << "\n";
224633c76c0cSRong Xu if (!IsIRInstr)
224733c76c0cSRong Xu OS << " Function count: " << Func.Counts[0] << "\n";
22486f7c19a4SXinliang David Li
22499e9a057aSJustin Bogner if (ShowIndirectCallTargets)
22502004f003SXinliang David Li OS << " Indirect Call Site Count: "
22512004f003SXinliang David Li << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n";
22529af28ef9SJustin Bogner
225360faea19SRong Xu uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize);
225460faea19SRong Xu if (ShowMemOPSizes && NumMemOPCalls > 0)
225560faea19SRong Xu OS << " Number of Memory Intrinsics Calls: " << NumMemOPCalls
225660faea19SRong Xu << "\n";
225760faea19SRong Xu
22586f7c19a4SXinliang David Li if (ShowCounts) {
22599af28ef9SJustin Bogner OS << " Block counts: [";
226033c76c0cSRong Xu size_t Start = (IsIRInstr ? 0 : 1);
226133c76c0cSRong Xu for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) {
226233c76c0cSRong Xu OS << (I == Start ? "" : ", ") << Func.Counts[I];
22639af28ef9SJustin Bogner }
22649af28ef9SJustin Bogner OS << "]\n";
22656f7c19a4SXinliang David Li }
22669e9a057aSJustin Bogner
22676f7c19a4SXinliang David Li if (ShowIndirectCallTargets) {
22689e9a057aSJustin Bogner OS << " Indirect Target Results:\n";
22690cf1f56aSRong Xu traverseAllValueSites(Func, IPVK_IndirectCallTarget,
22700cf1f56aSRong Xu VPStats[IPVK_IndirectCallTarget], OS,
227160faea19SRong Xu &(Reader->getSymtab()));
227260faea19SRong Xu }
227360faea19SRong Xu
227460faea19SRong Xu if (ShowMemOPSizes && NumMemOPCalls > 0) {
2275cd2aa0d2STeresa Johnson OS << " Memory Intrinsic Size Results:\n";
227660faea19SRong Xu traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS,
227760faea19SRong Xu nullptr);
22789e9a057aSJustin Bogner }
22799af28ef9SJustin Bogner }
22806f7c19a4SXinliang David Li }
2281db1225d0SJustin Bogner if (Reader->hasError())
22829152fd17SVedant Kumar exitWithError(Reader->getError(), Filename);
22839af28ef9SJustin Bogner
228411d30742SEllis Hoag if (TextFormat || ShowCovered)
22856f7c19a4SXinliang David Li return 0;
22867cefdb81SEaswaran Raman std::unique_ptr<ProfileSummary> PS(Builder.getSummary());
228750da55a5SRong Xu bool IsIR = Reader->isIRLevelProfile();
228850da55a5SRong Xu OS << "Instrumentation level: " << (IsIR ? "IR" : "Front-end");
228950da55a5SRong Xu if (IsIR)
229050da55a5SRong Xu OS << " entry_first = " << Reader->instrEntryBBEnabled();
229150da55a5SRong Xu OS << "\n";
22929af28ef9SJustin Bogner if (ShowAllFunctions || !ShowFunction.empty())
22939af28ef9SJustin Bogner OS << "Functions shown: " << ShownFunctions << "\n";
2294e5a17e3fSEaswaran Raman OS << "Total functions: " << PS->getNumFunctions() << "\n";
229552aa224aSRong Xu if (ValueCutoff > 0) {
229652aa224aSRong Xu OS << "Number of functions with maximum count (< " << ValueCutoff
229752aa224aSRong Xu << "): " << BelowCutoffFunctions << "\n";
229852aa224aSRong Xu OS << "Number of functions with maximum count (>= " << ValueCutoff
229952aa224aSRong Xu << "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n";
230052aa224aSRong Xu }
2301e5a17e3fSEaswaran Raman OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n";
23027cefdb81SEaswaran Raman OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n";
230360faea19SRong Xu
2304801b5319SXinliang David Li if (TopN) {
2305801b5319SXinliang David Li std::vector<std::pair<std::string, uint64_t>> SortedHottestFuncs;
2306801b5319SXinliang David Li while (!HottestFuncs.empty()) {
2307801b5319SXinliang David Li SortedHottestFuncs.emplace_back(HottestFuncs.top());
2308801b5319SXinliang David Li HottestFuncs.pop();
2309801b5319SXinliang David Li }
2310801b5319SXinliang David Li OS << "Top " << TopN
2311801b5319SXinliang David Li << " functions with the largest internal block counts: \n";
2312801b5319SXinliang David Li for (auto &hotfunc : llvm::reverse(SortedHottestFuncs))
2313801b5319SXinliang David Li OS << " " << hotfunc.first << ", max count = " << hotfunc.second << "\n";
2314801b5319SXinliang David Li }
2315801b5319SXinliang David Li
2316872362c4SXinliang David Li if (ShownFunctions && ShowIndirectCallTargets) {
23170cf1f56aSRong Xu OS << "Statistics for indirect call sites profile:\n";
23180cf1f56aSRong Xu showValueSitesStats(OS, IPVK_IndirectCallTarget,
23190cf1f56aSRong Xu VPStats[IPVK_IndirectCallTarget]);
2320872362c4SXinliang David Li }
2321183ebbe0SEaswaran Raman
232260faea19SRong Xu if (ShownFunctions && ShowMemOPSizes) {
232360faea19SRong Xu OS << "Statistics for memory intrinsic calls sizes profile:\n";
232460faea19SRong Xu showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]);
232560faea19SRong Xu }
232660faea19SRong Xu
2327183ebbe0SEaswaran Raman if (ShowDetailedSummary) {
23287cefdb81SEaswaran Raman OS << "Total number of blocks: " << PS->getNumCounts() << "\n";
2329e5a17e3fSEaswaran Raman OS << "Total count: " << PS->getTotalCount() << "\n";
233017fc6518SWenlei He PS->printDetailedSummary(OS);
2331183ebbe0SEaswaran Raman }
2332e50a3884SGulfem Savrun Yeniceri
2333e50a3884SGulfem Savrun Yeniceri if (ShowBinaryIds)
2334e50a3884SGulfem Savrun Yeniceri if (Error E = Reader->printBinaryIds(OS))
2335e50a3884SGulfem Savrun Yeniceri exitWithError(std::move(E), Filename);
2336e50a3884SGulfem Savrun Yeniceri
23379af28ef9SJustin Bogner return 0;
23389af28ef9SJustin Bogner }
23399af28ef9SJustin Bogner
showSectionInfo(sampleprof::SampleProfileReader * Reader,raw_fd_ostream & OS)2340eee532cdSWei Mi static void showSectionInfo(sampleprof::SampleProfileReader *Reader,
2341eee532cdSWei Mi raw_fd_ostream &OS) {
2342eee532cdSWei Mi if (!Reader->dumpSectionInfo(OS)) {
2343eee532cdSWei Mi WithColor::warning() << "-show-sec-info-only is only supported for "
2344eee532cdSWei Mi << "sample profile in extbinary format and is "
2345eee532cdSWei Mi << "ignored for other formats.\n";
2346eee532cdSWei Mi return;
2347eee532cdSWei Mi }
2348eee532cdSWei Mi }
2349eee532cdSWei Mi
2350546be088SFangrui Song namespace {
235153cf5302Sweihe struct HotFuncInfo {
2352b9db7036SHongtao Yu std::string FuncName;
235353cf5302Sweihe uint64_t TotalCount;
235453cf5302Sweihe double TotalCountPercent;
235553cf5302Sweihe uint64_t MaxCount;
235653cf5302Sweihe uint64_t EntryCount;
235753cf5302Sweihe
HotFuncInfo__anon73ab2c7b1111::HotFuncInfo235853cf5302Sweihe HotFuncInfo()
2359f44473ecSKazu Hirata : TotalCount(0), TotalCountPercent(0.0f), MaxCount(0), EntryCount(0) {}
236053cf5302Sweihe
HotFuncInfo__anon73ab2c7b1111::HotFuncInfo236153cf5302Sweihe HotFuncInfo(StringRef FN, uint64_t TS, double TSP, uint64_t MS, uint64_t ES)
2362b9db7036SHongtao Yu : FuncName(FN.begin(), FN.end()), TotalCount(TS), TotalCountPercent(TSP),
2363b9db7036SHongtao Yu MaxCount(MS), EntryCount(ES) {}
236453cf5302Sweihe };
2365546be088SFangrui Song } // namespace
236653cf5302Sweihe
236753cf5302Sweihe // Print out detailed information about hot functions in PrintValues vector.
236853cf5302Sweihe // Users specify titles and offset of every columns through ColumnTitle and
236953cf5302Sweihe // ColumnOffset. The size of ColumnTitle and ColumnOffset need to be the same
237053cf5302Sweihe // and at least 4. Besides, users can optionally give a HotFuncMetric string to
237153cf5302Sweihe // print out or let it be an empty string.
dumpHotFunctionList(const std::vector<std::string> & ColumnTitle,const std::vector<int> & ColumnOffset,const std::vector<HotFuncInfo> & PrintValues,uint64_t HotFuncCount,uint64_t TotalFuncCount,uint64_t HotProfCount,uint64_t TotalProfCount,const std::string & HotFuncMetric,uint32_t TopNFunctions,raw_fd_ostream & OS)237253cf5302Sweihe static void dumpHotFunctionList(const std::vector<std::string> &ColumnTitle,
237353cf5302Sweihe const std::vector<int> &ColumnOffset,
237453cf5302Sweihe const std::vector<HotFuncInfo> &PrintValues,
237553cf5302Sweihe uint64_t HotFuncCount, uint64_t TotalFuncCount,
237653cf5302Sweihe uint64_t HotProfCount, uint64_t TotalProfCount,
237753cf5302Sweihe const std::string &HotFuncMetric,
2378ce6ed64aSmodimo uint32_t TopNFunctions, raw_fd_ostream &OS) {
2379540489deSweihe assert(ColumnOffset.size() == ColumnTitle.size() &&
2380540489deSweihe "ColumnOffset and ColumnTitle should have the same size");
2381540489deSweihe assert(ColumnTitle.size() >= 4 &&
2382540489deSweihe "ColumnTitle should have at least 4 elements");
2383540489deSweihe assert(TotalFuncCount > 0 &&
2384540489deSweihe "There should be at least one function in the profile");
238553cf5302Sweihe double TotalProfPercent = 0;
238653cf5302Sweihe if (TotalProfCount > 0)
2387540489deSweihe TotalProfPercent = static_cast<double>(HotProfCount) / TotalProfCount * 100;
238853cf5302Sweihe
238953cf5302Sweihe formatted_raw_ostream FOS(OS);
239053cf5302Sweihe FOS << HotFuncCount << " out of " << TotalFuncCount
239153cf5302Sweihe << " functions with profile ("
2392540489deSweihe << format("%.2f%%",
2393540489deSweihe (static_cast<double>(HotFuncCount) / TotalFuncCount * 100))
239453cf5302Sweihe << ") are considered hot functions";
239553cf5302Sweihe if (!HotFuncMetric.empty())
239653cf5302Sweihe FOS << " (" << HotFuncMetric << ")";
239753cf5302Sweihe FOS << ".\n";
239853cf5302Sweihe FOS << HotProfCount << " out of " << TotalProfCount << " profile counts ("
239953cf5302Sweihe << format("%.2f%%", TotalProfPercent) << ") are from hot functions.\n";
240053cf5302Sweihe
240153cf5302Sweihe for (size_t I = 0; I < ColumnTitle.size(); ++I) {
240253cf5302Sweihe FOS.PadToColumn(ColumnOffset[I]);
240353cf5302Sweihe FOS << ColumnTitle[I];
240453cf5302Sweihe }
240553cf5302Sweihe FOS << "\n";
240653cf5302Sweihe
2407ce6ed64aSmodimo uint32_t Count = 0;
2408ce6ed64aSmodimo for (const auto &R : PrintValues) {
2409ce6ed64aSmodimo if (TopNFunctions && (Count++ == TopNFunctions))
2410ce6ed64aSmodimo break;
241153cf5302Sweihe FOS.PadToColumn(ColumnOffset[0]);
241253cf5302Sweihe FOS << R.TotalCount << " (" << format("%.2f%%", R.TotalCountPercent) << ")";
241353cf5302Sweihe FOS.PadToColumn(ColumnOffset[1]);
241453cf5302Sweihe FOS << R.MaxCount;
241553cf5302Sweihe FOS.PadToColumn(ColumnOffset[2]);
241653cf5302Sweihe FOS << R.EntryCount;
241753cf5302Sweihe FOS.PadToColumn(ColumnOffset[3]);
241853cf5302Sweihe FOS << R.FuncName << "\n";
241953cf5302Sweihe }
242053cf5302Sweihe }
242153cf5302Sweihe
showHotFunctionList(const sampleprof::SampleProfileMap & Profiles,ProfileSummary & PS,uint32_t TopN,raw_fd_ostream & OS)2422b9db7036SHongtao Yu static int showHotFunctionList(const sampleprof::SampleProfileMap &Profiles,
2423ce6ed64aSmodimo ProfileSummary &PS, uint32_t TopN,
2424ce6ed64aSmodimo raw_fd_ostream &OS) {
242553cf5302Sweihe using namespace sampleprof;
242653cf5302Sweihe
242753cf5302Sweihe const uint32_t HotFuncCutoff = 990000;
242853cf5302Sweihe auto &SummaryVector = PS.getDetailedSummary();
242953cf5302Sweihe uint64_t MinCountThreshold = 0;
2430546be088SFangrui Song for (const ProfileSummaryEntry &SummaryEntry : SummaryVector) {
243153cf5302Sweihe if (SummaryEntry.Cutoff == HotFuncCutoff) {
243253cf5302Sweihe MinCountThreshold = SummaryEntry.MinCount;
243353cf5302Sweihe break;
243453cf5302Sweihe }
243553cf5302Sweihe }
243653cf5302Sweihe
243753cf5302Sweihe // Traverse all functions in the profile and keep only hot functions.
243853cf5302Sweihe // The following loop also calculates the sum of total samples of all
243953cf5302Sweihe // functions.
244053cf5302Sweihe std::multimap<uint64_t, std::pair<const FunctionSamples *, const uint64_t>,
244153cf5302Sweihe std::greater<uint64_t>>
244253cf5302Sweihe HotFunc;
244353cf5302Sweihe uint64_t ProfileTotalSample = 0;
244453cf5302Sweihe uint64_t HotFuncSample = 0;
244553cf5302Sweihe uint64_t HotFuncCount = 0;
2446540489deSweihe
244753cf5302Sweihe for (const auto &I : Profiles) {
2448540489deSweihe FuncSampleStats FuncStats;
2449546be088SFangrui Song const FunctionSamples &FuncProf = I.second;
245053cf5302Sweihe ProfileTotalSample += FuncProf.getTotalSamples();
2451540489deSweihe getFuncSampleStats(FuncProf, FuncStats, MinCountThreshold);
245253cf5302Sweihe
2453540489deSweihe if (isFunctionHot(FuncStats, MinCountThreshold)) {
245453cf5302Sweihe HotFunc.emplace(FuncProf.getTotalSamples(),
2455540489deSweihe std::make_pair(&(I.second), FuncStats.MaxSample));
245653cf5302Sweihe HotFuncSample += FuncProf.getTotalSamples();
245753cf5302Sweihe ++HotFuncCount;
245853cf5302Sweihe }
245953cf5302Sweihe }
246053cf5302Sweihe
246153cf5302Sweihe std::vector<std::string> ColumnTitle{"Total sample (%)", "Max sample",
246253cf5302Sweihe "Entry sample", "Function name"};
246353cf5302Sweihe std::vector<int> ColumnOffset{0, 24, 42, 58};
246453cf5302Sweihe std::string Metric =
246553cf5302Sweihe std::string("max sample >= ") + std::to_string(MinCountThreshold);
246653cf5302Sweihe std::vector<HotFuncInfo> PrintValues;
246753cf5302Sweihe for (const auto &FuncPair : HotFunc) {
2468546be088SFangrui Song const FunctionSamples &Func = *FuncPair.second.first;
246953cf5302Sweihe double TotalSamplePercent =
247053cf5302Sweihe (ProfileTotalSample > 0)
2471546be088SFangrui Song ? (Func.getTotalSamples() * 100.0) / ProfileTotalSample
247253cf5302Sweihe : 0;
24737b81a81dSMircea Trofin PrintValues.emplace_back(
24747b81a81dSMircea Trofin HotFuncInfo(Func.getContext().toString(), Func.getTotalSamples(),
24757b81a81dSMircea Trofin TotalSamplePercent, FuncPair.second.second,
24767b81a81dSMircea Trofin Func.getHeadSamplesEstimate()));
247753cf5302Sweihe }
247853cf5302Sweihe dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount,
247953cf5302Sweihe Profiles.size(), HotFuncSample, ProfileTotalSample,
2480ce6ed64aSmodimo Metric, TopN, OS);
248153cf5302Sweihe
248253cf5302Sweihe return 0;
248353cf5302Sweihe }
248453cf5302Sweihe
showSampleProfile(const std::string & Filename,bool ShowCounts,uint32_t TopN,bool ShowAllFunctions,bool ShowDetailedSummary,const std::string & ShowFunction,bool ShowProfileSymbolList,bool ShowSectionInfoOnly,bool ShowHotFuncList,raw_fd_ostream & OS)24851afc1de4SBenjamin Kramer static int showSampleProfile(const std::string &Filename, bool ShowCounts,
2486ce6ed64aSmodimo uint32_t TopN, bool ShowAllFunctions,
2487ce6ed64aSmodimo bool ShowDetailedSummary,
24881afc1de4SBenjamin Kramer const std::string &ShowFunction,
2489eee532cdSWei Mi bool ShowProfileSymbolList,
249053cf5302Sweihe bool ShowSectionInfoOnly, bool ShowHotFuncList,
249153cf5302Sweihe raw_fd_ostream &OS) {
2492d5336ae2SDiego Novillo using namespace sampleprof;
249303b42e41SMehdi Amini LLVMContext Context;
24948d581857SRong Xu auto ReaderOrErr =
24958d581857SRong Xu SampleProfileReader::create(Filename, Context, FSDiscriminatorPassOption);
2496fcd55607SDiego Novillo if (std::error_code EC = ReaderOrErr.getError())
24974f823667SNathan Slingerland exitWithErrorCode(EC, Filename);
2498d5336ae2SDiego Novillo
2499fcd55607SDiego Novillo auto Reader = std::move(ReaderOrErr.get());
2500eee532cdSWei Mi if (ShowSectionInfoOnly) {
2501eee532cdSWei Mi showSectionInfo(Reader.get(), OS);
2502eee532cdSWei Mi return 0;
2503eee532cdSWei Mi }
2504eee532cdSWei Mi
2505c6d032abSDiego Novillo if (std::error_code EC = Reader->read())
25064f823667SNathan Slingerland exitWithErrorCode(EC, Filename);
2507c6d032abSDiego Novillo
2508d5336ae2SDiego Novillo if (ShowAllFunctions || ShowFunction.empty())
2509d5336ae2SDiego Novillo Reader->dump(OS);
2510d5336ae2SDiego Novillo else
2511b9db7036SHongtao Yu // TODO: parse context string to support filtering by contexts.
2512b9db7036SHongtao Yu Reader->dumpFunctionProfile(StringRef(ShowFunction), OS);
2513d5336ae2SDiego Novillo
2514798e59b8SWei Mi if (ShowProfileSymbolList) {
2515798e59b8SWei Mi std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =
2516798e59b8SWei Mi Reader->getProfileSymbolList();
2517798e59b8SWei Mi ReaderList->dump(OS);
2518798e59b8SWei Mi }
2519798e59b8SWei Mi
252017fc6518SWenlei He if (ShowDetailedSummary) {
252117fc6518SWenlei He auto &PS = Reader->getSummary();
252217fc6518SWenlei He PS.printSummary(OS);
252317fc6518SWenlei He PS.printDetailedSummary(OS);
252417fc6518SWenlei He }
252517fc6518SWenlei He
2526ce6ed64aSmodimo if (ShowHotFuncList || TopN)
2527ce6ed64aSmodimo showHotFunctionList(Reader->getProfiles(), Reader->getSummary(), TopN, OS);
252853cf5302Sweihe
2529d5336ae2SDiego Novillo return 0;
2530d5336ae2SDiego Novillo }
2531d5336ae2SDiego Novillo
showMemProfProfile(const std::string & Filename,const std::string & ProfiledBinary,raw_fd_ostream & OS)2532216575e5SSnehasish Kumar static int showMemProfProfile(const std::string &Filename,
2533216575e5SSnehasish Kumar const std::string &ProfiledBinary,
2534216575e5SSnehasish Kumar raw_fd_ostream &OS) {
2535ec51971eSSnehasish Kumar auto ReaderOr = llvm::memprof::RawMemProfReader::create(
2536ec51971eSSnehasish Kumar Filename, ProfiledBinary, /*KeepNames=*/true);
25377cca33b4SSnehasish Kumar if (Error E = ReaderOr.takeError())
2538216575e5SSnehasish Kumar // Since the error can be related to the profile or the binary we do not
2539216575e5SSnehasish Kumar // pass whence. Instead additional context is provided where necessary in
2540216575e5SSnehasish Kumar // the error message.
2541216575e5SSnehasish Kumar exitWithError(std::move(E), /*Whence*/ "");
25427cca33b4SSnehasish Kumar
25437cca33b4SSnehasish Kumar std::unique_ptr<llvm::memprof::RawMemProfReader> Reader(
25447cca33b4SSnehasish Kumar ReaderOr.get().release());
254514f4f63aSSnehasish Kumar
254614f4f63aSSnehasish Kumar Reader->printYAML(OS);
25477cca33b4SSnehasish Kumar return 0;
25487cca33b4SSnehasish Kumar }
25497cca33b4SSnehasish Kumar
showDebugInfoCorrelation(const std::string & Filename,bool ShowDetailedSummary,bool ShowProfileSymbolList,raw_fd_ostream & OS)2550c9baa560SEllis Hoag static int showDebugInfoCorrelation(const std::string &Filename,
2551c9baa560SEllis Hoag bool ShowDetailedSummary,
2552c9baa560SEllis Hoag bool ShowProfileSymbolList,
2553c9baa560SEllis Hoag raw_fd_ostream &OS) {
2554c9baa560SEllis Hoag std::unique_ptr<InstrProfCorrelator> Correlator;
2555c9baa560SEllis Hoag if (auto Err = InstrProfCorrelator::get(Filename).moveInto(Correlator))
2556c9baa560SEllis Hoag exitWithError(std::move(Err), Filename);
2557c9baa560SEllis Hoag if (auto Err = Correlator->correlateProfileData())
2558c9baa560SEllis Hoag exitWithError(std::move(Err), Filename);
2559c9baa560SEllis Hoag
2560c9baa560SEllis Hoag InstrProfSymtab Symtab;
2561c9baa560SEllis Hoag if (auto Err = Symtab.create(
2562c9baa560SEllis Hoag StringRef(Correlator->getNamesPointer(), Correlator->getNamesSize())))
2563c9baa560SEllis Hoag exitWithError(std::move(Err), Filename);
2564c9baa560SEllis Hoag
2565c9baa560SEllis Hoag if (ShowProfileSymbolList)
2566c9baa560SEllis Hoag Symtab.dumpNames(OS);
2567c9baa560SEllis Hoag // TODO: Read "Profile Data Type" from debug info to compute and show how many
2568c9baa560SEllis Hoag // counters the section holds.
2569c9baa560SEllis Hoag if (ShowDetailedSummary)
2570c9baa560SEllis Hoag OS << "Counters section size: 0x"
2571c9baa560SEllis Hoag << Twine::utohexstr(Correlator->getCountersSectionSize()) << " bytes\n";
2572c9baa560SEllis Hoag OS << "Found " << Correlator->getDataSize() << " functions\n";
2573c9baa560SEllis Hoag
2574c9baa560SEllis Hoag return 0;
2575c9baa560SEllis Hoag }
2576c9baa560SEllis Hoag
show_main(int argc,const char * argv[])2577f044d3f9SBenjamin Kramer static int show_main(int argc, const char *argv[]) {
2578c9baa560SEllis Hoag cl::opt<std::string> Filename(cl::Positional, cl::desc("<profdata-file>"));
2579d5336ae2SDiego Novillo
2580d5336ae2SDiego Novillo cl::opt<bool> ShowCounts("counts", cl::init(false),
2581d5336ae2SDiego Novillo cl::desc("Show counter values for shown functions"));
25826f7c19a4SXinliang David Li cl::opt<bool> TextFormat(
25836f7c19a4SXinliang David Li "text", cl::init(false),
25846f7c19a4SXinliang David Li cl::desc("Show instr profile data in text dump format"));
25859e9a057aSJustin Bogner cl::opt<bool> ShowIndirectCallTargets(
25869e9a057aSJustin Bogner "ic-targets", cl::init(false),
25879e9a057aSJustin Bogner cl::desc("Show indirect call site target values for shown functions"));
258860faea19SRong Xu cl::opt<bool> ShowMemOPSizes(
258960faea19SRong Xu "memop-sizes", cl::init(false),
259060faea19SRong Xu cl::desc("Show the profiled sizes of the memory intrinsic calls "
259160faea19SRong Xu "for shown functions"));
2592183ebbe0SEaswaran Raman cl::opt<bool> ShowDetailedSummary("detailed-summary", cl::init(false),
2593183ebbe0SEaswaran Raman cl::desc("Show detailed profile summary"));
2594183ebbe0SEaswaran Raman cl::list<uint32_t> DetailedSummaryCutoffs(
2595183ebbe0SEaswaran Raman cl::CommaSeparated, "detailed-summary-cutoffs",
2596183ebbe0SEaswaran Raman cl::desc(
2597183ebbe0SEaswaran Raman "Cutoff percentages (times 10000) for generating detailed summary"),
2598183ebbe0SEaswaran Raman cl::value_desc("800000,901000,999999"));
259953cf5302Sweihe cl::opt<bool> ShowHotFuncList(
260053cf5302Sweihe "hot-func-list", cl::init(false),
260153cf5302Sweihe cl::desc("Show profile summary of a list of hot functions"));
2602d5336ae2SDiego Novillo cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false),
2603d5336ae2SDiego Novillo cl::desc("Details for every function"));
2604a6ff69f6SRong Xu cl::opt<bool> ShowCS("showcs", cl::init(false),
2605a6ff69f6SRong Xu cl::desc("Show context sensitive counts"));
2606d5336ae2SDiego Novillo cl::opt<std::string> ShowFunction("function",
2607d5336ae2SDiego Novillo cl::desc("Details for matching functions"));
2608d5336ae2SDiego Novillo
2609d5336ae2SDiego Novillo cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
2610d5336ae2SDiego Novillo cl::init("-"), cl::desc("Output file"));
2611d5336ae2SDiego Novillo cl::alias OutputFilenameA("o", cl::desc("Alias for --output"),
2612d5336ae2SDiego Novillo cl::aliasopt(OutputFilename));
2613d5336ae2SDiego Novillo cl::opt<ProfileKinds> ProfileKind(
2614d5336ae2SDiego Novillo cl::desc("Profile kind:"), cl::init(instr),
2615d5336ae2SDiego Novillo cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
26167cca33b4SSnehasish Kumar clEnumVal(sample, "Sample profile"),
26177cca33b4SSnehasish Kumar clEnumVal(memory, "MemProf memory access profile")));
2618801b5319SXinliang David Li cl::opt<uint32_t> TopNFunctions(
2619801b5319SXinliang David Li "topn", cl::init(0),
2620801b5319SXinliang David Li cl::desc("Show the list of functions with the largest internal counts"));
262152aa224aSRong Xu cl::opt<uint32_t> ValueCutoff(
262252aa224aSRong Xu "value-cutoff", cl::init(0),
262352aa224aSRong Xu cl::desc("Set the count value cutoff. Functions with the maximum count "
262452aa224aSRong Xu "less than this value will not be printed out. (Default is 0)"));
262552aa224aSRong Xu cl::opt<bool> OnlyListBelow(
262652aa224aSRong Xu "list-below-cutoff", cl::init(false),
262752aa224aSRong Xu cl::desc("Only output names of functions whose max count values are "
262852aa224aSRong Xu "below the cutoff value"));
2629798e59b8SWei Mi cl::opt<bool> ShowProfileSymbolList(
2630798e59b8SWei Mi "show-prof-sym-list", cl::init(false),
2631798e59b8SWei Mi cl::desc("Show profile symbol list if it exists in the profile. "));
2632eee532cdSWei Mi cl::opt<bool> ShowSectionInfoOnly(
2633eee532cdSWei Mi "show-sec-info-only", cl::init(false),
2634eee532cdSWei Mi cl::desc("Show the information of each section in the sample profile. "
2635eee532cdSWei Mi "The flag is only usable when the sample profile is in "
2636eee532cdSWei Mi "extbinary format"));
2637e50a3884SGulfem Savrun Yeniceri cl::opt<bool> ShowBinaryIds("binary-ids", cl::init(false),
2638e50a3884SGulfem Savrun Yeniceri cl::desc("Show binary ids in the profile. "));
2639c9baa560SEllis Hoag cl::opt<std::string> DebugInfoFilename(
2640c9baa560SEllis Hoag "debug-info", cl::init(""),
2641c9baa560SEllis Hoag cl::desc("Read and extract profile metadata from debug info and show "
2642c9baa560SEllis Hoag "the functions it found."));
264311d30742SEllis Hoag cl::opt<bool> ShowCovered(
264411d30742SEllis Hoag "covered", cl::init(false),
264511d30742SEllis Hoag cl::desc("Show only the functions that have been executed."));
2646216575e5SSnehasish Kumar cl::opt<std::string> ProfiledBinary(
2647216575e5SSnehasish Kumar "profiled-binary", cl::init(""),
2648216575e5SSnehasish Kumar cl::desc("Path to binary from which the profile was collected."));
2649798e59b8SWei Mi
2650d5336ae2SDiego Novillo cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n");
2651d5336ae2SDiego Novillo
2652c9baa560SEllis Hoag if (Filename.empty() && DebugInfoFilename.empty())
2653c9baa560SEllis Hoag exitWithError(
2654c9baa560SEllis Hoag "the positional argument '<profdata-file>' is required unless '--" +
2655c9baa560SEllis Hoag DebugInfoFilename.ArgStr + "' is provided");
2656c9baa560SEllis Hoag
2657e5b4dbabSKazu Hirata if (Filename == OutputFilename) {
2658f0d3dcecSRong Xu errs() << sys::path::filename(argv[0])
2659f0d3dcecSRong Xu << ": Input file name cannot be the same as the output file name!\n";
2660f0d3dcecSRong Xu return 1;
2661f0d3dcecSRong Xu }
2662f0d3dcecSRong Xu
2663d5336ae2SDiego Novillo std::error_code EC;
266482b3e28eSAbhina Sreeskantharajan raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);
2665d5336ae2SDiego Novillo if (EC)
26664f823667SNathan Slingerland exitWithErrorCode(EC, OutputFilename);
2667d5336ae2SDiego Novillo
2668d5336ae2SDiego Novillo if (ShowAllFunctions && !ShowFunction.empty())
2669e46b7565SJonas Devlieghere WithColor::warning() << "-function argument ignored: showing all functions\n";
2670d5336ae2SDiego Novillo
2671c9baa560SEllis Hoag if (!DebugInfoFilename.empty())
2672c9baa560SEllis Hoag return showDebugInfoCorrelation(DebugInfoFilename, ShowDetailedSummary,
2673c9baa560SEllis Hoag ShowProfileSymbolList, OS);
2674c9baa560SEllis Hoag
2675d5336ae2SDiego Novillo if (ProfileKind == instr)
2676e50a3884SGulfem Savrun Yeniceri return showInstrProfile(
2677e50a3884SGulfem Savrun Yeniceri Filename, ShowCounts, TopNFunctions, ShowIndirectCallTargets,
2678e50a3884SGulfem Savrun Yeniceri ShowMemOPSizes, ShowDetailedSummary, DetailedSummaryCutoffs,
2679e50a3884SGulfem Savrun Yeniceri ShowAllFunctions, ShowCS, ValueCutoff, OnlyListBelow, ShowFunction,
268011d30742SEllis Hoag TextFormat, ShowBinaryIds, ShowCovered, OS);
26817cca33b4SSnehasish Kumar if (ProfileKind == sample)
2682ce6ed64aSmodimo return showSampleProfile(Filename, ShowCounts, TopNFunctions,
2683ce6ed64aSmodimo ShowAllFunctions, ShowDetailedSummary,
2684ce6ed64aSmodimo ShowFunction, ShowProfileSymbolList,
2685ce6ed64aSmodimo ShowSectionInfoOnly, ShowHotFuncList, OS);
2686216575e5SSnehasish Kumar return showMemProfProfile(Filename, ProfiledBinary, OS);
2687d5336ae2SDiego Novillo }
2688d5336ae2SDiego Novillo
main(int argc,const char * argv[])2689618bcea7SJustin Bogner int main(int argc, const char *argv[]) {
2690197194b6SRui Ueyama InitLLVM X(argc, argv);
2691618bcea7SJustin Bogner
2692618bcea7SJustin Bogner StringRef ProgName(sys::path::filename(argv[0]));
2693618bcea7SJustin Bogner if (argc > 1) {
2694e6cb63e4SCraig Topper int (*func)(int, const char *[]) = nullptr;
2695618bcea7SJustin Bogner
2696618bcea7SJustin Bogner if (strcmp(argv[1], "merge") == 0)
2697618bcea7SJustin Bogner func = merge_main;
26989af28ef9SJustin Bogner else if (strcmp(argv[1], "show") == 0)
26999af28ef9SJustin Bogner func = show_main;
2700998b97f6SRong Xu else if (strcmp(argv[1], "overlap") == 0)
2701998b97f6SRong Xu func = overlap_main;
2702618bcea7SJustin Bogner
2703618bcea7SJustin Bogner if (func) {
2704618bcea7SJustin Bogner std::string Invocation(ProgName.str() + " " + argv[1]);
2705618bcea7SJustin Bogner argv[1] = Invocation.c_str();
2706618bcea7SJustin Bogner return func(argc - 1, argv + 1);
2707618bcea7SJustin Bogner }
2708618bcea7SJustin Bogner
2709d3babdbcSDiego Novillo if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 ||
2710618bcea7SJustin Bogner strcmp(argv[1], "--help") == 0) {
2711618bcea7SJustin Bogner
2712618bcea7SJustin Bogner errs() << "OVERVIEW: LLVM profile data tools\n\n"
2713618bcea7SJustin Bogner << "USAGE: " << ProgName << " <command> [args...]\n"
2714618bcea7SJustin Bogner << "USAGE: " << ProgName << " <command> -help\n\n"
2715253eb17bSJustin Bogner << "See each individual command --help for more details.\n"
2716998b97f6SRong Xu << "Available commands: merge, show, overlap\n";
2717618bcea7SJustin Bogner return 0;
2718618bcea7SJustin Bogner }
2719618bcea7SJustin Bogner }
2720618bcea7SJustin Bogner
2721618bcea7SJustin Bogner if (argc < 2)
2722618bcea7SJustin Bogner errs() << ProgName << ": No command specified!\n";
2723618bcea7SJustin Bogner else
2724618bcea7SJustin Bogner errs() << ProgName << ": Unknown command!\n";
2725618bcea7SJustin Bogner
2726998b97f6SRong Xu errs() << "USAGE: " << ProgName << " <merge|show|overlap> [args...]\n";
2727618bcea7SJustin Bogner return 1;
2728618bcea7SJustin Bogner }
2729