10b57cec5SDimitry Andric //===- llvm-profdata.cpp - LLVM profile data tool -------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // llvm-profdata merges .profdata files.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric #include "llvm/ADT/SmallSet.h"
140b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h"
150b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
160b57cec5SDimitry Andric #include "llvm/IR/LLVMContext.h"
170eae32dcSDimitry Andric #include "llvm/Object/Binary.h"
180eae32dcSDimitry Andric #include "llvm/ProfileData/InstrProfCorrelator.h"
190b57cec5SDimitry Andric #include "llvm/ProfileData/InstrProfReader.h"
200b57cec5SDimitry Andric #include "llvm/ProfileData/InstrProfWriter.h"
2181ad6265SDimitry Andric #include "llvm/ProfileData/MemProf.h"
220b57cec5SDimitry Andric #include "llvm/ProfileData/ProfileCommon.h"
234824e7fdSDimitry Andric #include "llvm/ProfileData/RawMemProfReader.h"
240b57cec5SDimitry Andric #include "llvm/ProfileData/SampleProfReader.h"
250b57cec5SDimitry Andric #include "llvm/ProfileData/SampleProfWriter.h"
2606c3fb27SDimitry Andric #include "llvm/Support/BalancedPartitioning.h"
270b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h"
28fe6060f1SDimitry Andric #include "llvm/Support/Discriminator.h"
290b57cec5SDimitry Andric #include "llvm/Support/Errc.h"
300b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h"
310b57cec5SDimitry Andric #include "llvm/Support/Format.h"
325ffd83dbSDimitry Andric #include "llvm/Support/FormattedStream.h"
3306c3fb27SDimitry Andric #include "llvm/Support/LLVMDriver.h"
34bdd1243dSDimitry Andric #include "llvm/Support/MD5.h"
350b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
360b57cec5SDimitry Andric #include "llvm/Support/Path.h"
377a6dacacSDimitry Andric #include "llvm/Support/Regex.h"
380b57cec5SDimitry Andric #include "llvm/Support/ThreadPool.h"
395ffd83dbSDimitry Andric #include "llvm/Support/Threading.h"
4006c3fb27SDimitry Andric #include "llvm/Support/VirtualFileSystem.h"
410b57cec5SDimitry Andric #include "llvm/Support/WithColor.h"
420b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
430b57cec5SDimitry Andric #include <algorithm>
44bdd1243dSDimitry Andric #include <cmath>
45bdd1243dSDimitry Andric #include <optional>
4681ad6265SDimitry Andric #include <queue>
470b57cec5SDimitry Andric 
480b57cec5SDimitry Andric using namespace llvm;
495f757f3fSDimitry Andric using ProfCorrelatorKind = InstrProfCorrelator::ProfCorrelatorKind;
500b57cec5SDimitry Andric 
515f757f3fSDimitry Andric // https://llvm.org/docs/CommandGuide/llvm-profdata.html has documentations
525f757f3fSDimitry Andric // on each subcommand.
535f757f3fSDimitry Andric cl::SubCommand ShowSubcommand(
545f757f3fSDimitry Andric     "show",
555f757f3fSDimitry Andric     "Takes a profile data file and displays the profiles. See detailed "
565f757f3fSDimitry Andric     "documentation in "
575f757f3fSDimitry Andric     "https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-show");
585f757f3fSDimitry Andric cl::SubCommand OrderSubcommand(
595f757f3fSDimitry Andric     "order",
605f757f3fSDimitry Andric     "Reads temporal profiling traces from a profile and outputs a function "
615f757f3fSDimitry Andric     "order that reduces the number of page faults for those traces. See "
625f757f3fSDimitry Andric     "detailed documentation in "
635f757f3fSDimitry Andric     "https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-order");
645f757f3fSDimitry Andric cl::SubCommand OverlapSubcommand(
655f757f3fSDimitry Andric     "overlap",
665f757f3fSDimitry Andric     "Computes and displays the overlap between two profiles. See detailed "
675f757f3fSDimitry Andric     "documentation in "
685f757f3fSDimitry Andric     "https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-overlap");
695f757f3fSDimitry Andric cl::SubCommand MergeSubcommand(
705f757f3fSDimitry Andric     "merge",
715f757f3fSDimitry Andric     "Takes several profiles and merge them together. See detailed "
725f757f3fSDimitry Andric     "documentation in "
735f757f3fSDimitry Andric     "https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-merge");
745f757f3fSDimitry Andric 
755f757f3fSDimitry Andric namespace {
765f757f3fSDimitry Andric enum ProfileKinds { instr, sample, memory };
775f757f3fSDimitry Andric enum FailureMode { warnOnly, failIfAnyAreInvalid, failIfAllAreInvalid };
785f757f3fSDimitry Andric } // namespace
79bdd1243dSDimitry Andric 
800b57cec5SDimitry Andric enum ProfileFormat {
810b57cec5SDimitry Andric   PF_None = 0,
820b57cec5SDimitry Andric   PF_Text,
8306c3fb27SDimitry Andric   PF_Compact_Binary, // Deprecated
848bcb0991SDimitry Andric   PF_Ext_Binary,
850b57cec5SDimitry Andric   PF_GCC,
860b57cec5SDimitry Andric   PF_Binary
870b57cec5SDimitry Andric };
880b57cec5SDimitry Andric 
89bdd1243dSDimitry Andric enum class ShowFormat { Text, Json, Yaml };
90bdd1243dSDimitry Andric 
915f757f3fSDimitry Andric // Common options.
925f757f3fSDimitry Andric cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
935f757f3fSDimitry Andric                                     cl::init("-"), cl::desc("Output file"),
945f757f3fSDimitry Andric                                     cl::sub(ShowSubcommand),
955f757f3fSDimitry Andric                                     cl::sub(OrderSubcommand),
965f757f3fSDimitry Andric                                     cl::sub(OverlapSubcommand),
975f757f3fSDimitry Andric                                     cl::sub(MergeSubcommand));
985f757f3fSDimitry Andric // NOTE: cl::alias must not have cl::sub(), since aliased option's cl::sub()
995f757f3fSDimitry Andric // will be used. llvm::cl::alias::done() method asserts this condition.
1005f757f3fSDimitry Andric cl::alias OutputFilenameA("o", cl::desc("Alias for --output"),
1015f757f3fSDimitry Andric                           cl::aliasopt(OutputFilename));
1025f757f3fSDimitry Andric 
1035f757f3fSDimitry Andric // Options common to at least two commands.
1045f757f3fSDimitry Andric cl::opt<ProfileKinds> ProfileKind(
1055f757f3fSDimitry Andric     cl::desc("Profile kind:"), cl::sub(MergeSubcommand),
1065f757f3fSDimitry Andric     cl::sub(OverlapSubcommand), cl::init(instr),
1075f757f3fSDimitry Andric     cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
1085f757f3fSDimitry Andric                clEnumVal(sample, "Sample profile")));
1095f757f3fSDimitry Andric cl::opt<std::string> Filename(cl::Positional, cl::desc("<profdata-file>"),
1105f757f3fSDimitry Andric                               cl::sub(ShowSubcommand),
1115f757f3fSDimitry Andric                               cl::sub(OrderSubcommand));
1125f757f3fSDimitry Andric cl::opt<unsigned> MaxDbgCorrelationWarnings(
1135f757f3fSDimitry Andric     "max-debug-info-correlation-warnings",
1145f757f3fSDimitry Andric     cl::desc("The maximum number of warnings to emit when correlating "
1155f757f3fSDimitry Andric              "profile from debug info (0 = no limit)"),
1165f757f3fSDimitry Andric     cl::sub(MergeSubcommand), cl::sub(ShowSubcommand), cl::init(5));
1175f757f3fSDimitry Andric cl::opt<std::string> ProfiledBinary(
1185f757f3fSDimitry Andric     "profiled-binary", cl::init(""),
1195f757f3fSDimitry Andric     cl::desc("Path to binary from which the profile was collected."),
1205f757f3fSDimitry Andric     cl::sub(ShowSubcommand), cl::sub(MergeSubcommand));
1215f757f3fSDimitry Andric cl::opt<std::string> DebugInfoFilename(
1225f757f3fSDimitry Andric     "debug-info", cl::init(""),
1235f757f3fSDimitry Andric     cl::desc(
1245f757f3fSDimitry Andric         "For show, read and extract profile metadata from debug info and show "
1255f757f3fSDimitry Andric         "the functions it found. For merge, use the provided debug info to "
1265f757f3fSDimitry Andric         "correlate the raw profile."),
1275f757f3fSDimitry Andric     cl::sub(ShowSubcommand), cl::sub(MergeSubcommand));
1285f757f3fSDimitry Andric cl::opt<std::string>
1295f757f3fSDimitry Andric     BinaryFilename("binary-file", cl::init(""),
1305f757f3fSDimitry Andric                    cl::desc("For merge, use the provided unstripped bianry to "
1315f757f3fSDimitry Andric                             "correlate the raw profile."),
1325f757f3fSDimitry Andric                    cl::sub(MergeSubcommand));
1335f757f3fSDimitry Andric cl::opt<std::string> FuncNameFilter(
1345f757f3fSDimitry Andric     "function",
1357a6dacacSDimitry Andric     cl::desc("Only functions matching the filter are shown in the output. For "
1367a6dacacSDimitry Andric              "overlapping CSSPGO, this takes a function name with calling "
1377a6dacacSDimitry Andric              "context."),
1387a6dacacSDimitry Andric     cl::sub(ShowSubcommand), cl::sub(OverlapSubcommand),
1397a6dacacSDimitry Andric     cl::sub(MergeSubcommand));
1405f757f3fSDimitry Andric 
1415f757f3fSDimitry Andric // TODO: Consider creating a template class (e.g., MergeOption, ShowOption) to
1425f757f3fSDimitry Andric // factor out the common cl::sub in cl::opt constructor for subcommand-specific
1435f757f3fSDimitry Andric // options.
1445f757f3fSDimitry Andric 
1455f757f3fSDimitry Andric // Options specific to merge subcommand.
1465f757f3fSDimitry Andric cl::list<std::string> InputFilenames(cl::Positional, cl::sub(MergeSubcommand),
1475f757f3fSDimitry Andric                                      cl::desc("<filename...>"));
1485f757f3fSDimitry Andric cl::list<std::string> WeightedInputFilenames("weighted-input",
1495f757f3fSDimitry Andric                                              cl::sub(MergeSubcommand),
1505f757f3fSDimitry Andric                                              cl::desc("<weight>,<filename>"));
1515f757f3fSDimitry Andric cl::opt<ProfileFormat> OutputFormat(
1525f757f3fSDimitry Andric     cl::desc("Format of output profile"), cl::sub(MergeSubcommand),
1535f757f3fSDimitry Andric     cl::init(PF_Ext_Binary),
1545f757f3fSDimitry Andric     cl::values(clEnumValN(PF_Binary, "binary", "Binary encoding"),
1555f757f3fSDimitry Andric                clEnumValN(PF_Ext_Binary, "extbinary",
1565f757f3fSDimitry Andric                           "Extensible binary encoding "
1575f757f3fSDimitry Andric                           "(default)"),
1585f757f3fSDimitry Andric                clEnumValN(PF_Text, "text", "Text encoding"),
1595f757f3fSDimitry Andric                clEnumValN(PF_GCC, "gcc",
1605f757f3fSDimitry Andric                           "GCC encoding (only meaningful for -sample)")));
1615f757f3fSDimitry Andric cl::opt<std::string>
1625f757f3fSDimitry Andric     InputFilenamesFile("input-files", cl::init(""), cl::sub(MergeSubcommand),
1635f757f3fSDimitry Andric                        cl::desc("Path to file containing newline-separated "
1645f757f3fSDimitry Andric                                 "[<weight>,]<filename> entries"));
1655f757f3fSDimitry Andric cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"),
1665f757f3fSDimitry Andric                               cl::aliasopt(InputFilenamesFile));
1675f757f3fSDimitry Andric cl::opt<bool> DumpInputFileList(
1685f757f3fSDimitry Andric     "dump-input-file-list", cl::init(false), cl::Hidden,
1695f757f3fSDimitry Andric     cl::sub(MergeSubcommand),
1705f757f3fSDimitry Andric     cl::desc("Dump the list of input files and their weights, then exit"));
1715f757f3fSDimitry Andric cl::opt<std::string> RemappingFile("remapping-file", cl::value_desc("file"),
1725f757f3fSDimitry Andric                                    cl::sub(MergeSubcommand),
1735f757f3fSDimitry Andric                                    cl::desc("Symbol remapping file"));
1745f757f3fSDimitry Andric cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"),
1755f757f3fSDimitry Andric                          cl::aliasopt(RemappingFile));
1765f757f3fSDimitry Andric cl::opt<bool>
1775f757f3fSDimitry Andric     UseMD5("use-md5", cl::init(false), cl::Hidden,
1785f757f3fSDimitry Andric            cl::desc("Choose to use MD5 to represent string in name table (only "
1795f757f3fSDimitry Andric                     "meaningful for -extbinary)"),
1805f757f3fSDimitry Andric            cl::sub(MergeSubcommand));
1815f757f3fSDimitry Andric cl::opt<bool> CompressAllSections(
1825f757f3fSDimitry Andric     "compress-all-sections", cl::init(false), cl::Hidden,
1835f757f3fSDimitry Andric     cl::sub(MergeSubcommand),
1845f757f3fSDimitry Andric     cl::desc("Compress all sections when writing the profile (only "
1855f757f3fSDimitry Andric              "meaningful for -extbinary)"));
1865f757f3fSDimitry Andric cl::opt<bool> SampleMergeColdContext(
1875f757f3fSDimitry Andric     "sample-merge-cold-context", cl::init(false), cl::Hidden,
1885f757f3fSDimitry Andric     cl::sub(MergeSubcommand),
1895f757f3fSDimitry Andric     cl::desc(
1905f757f3fSDimitry Andric         "Merge context sample profiles whose count is below cold threshold"));
1915f757f3fSDimitry Andric cl::opt<bool> SampleTrimColdContext(
1925f757f3fSDimitry Andric     "sample-trim-cold-context", cl::init(false), cl::Hidden,
1935f757f3fSDimitry Andric     cl::sub(MergeSubcommand),
1945f757f3fSDimitry Andric     cl::desc(
1955f757f3fSDimitry Andric         "Trim context sample profiles whose count is below cold threshold"));
1965f757f3fSDimitry Andric cl::opt<uint32_t> SampleColdContextFrameDepth(
1975f757f3fSDimitry Andric     "sample-frame-depth-for-cold-context", cl::init(1),
1985f757f3fSDimitry Andric     cl::sub(MergeSubcommand),
1995f757f3fSDimitry Andric     cl::desc("Keep the last K frames while merging cold profile. 1 means the "
2005f757f3fSDimitry Andric              "context-less base profile"));
2015f757f3fSDimitry Andric cl::opt<size_t> OutputSizeLimit(
2025f757f3fSDimitry Andric     "output-size-limit", cl::init(0), cl::Hidden, cl::sub(MergeSubcommand),
2035f757f3fSDimitry Andric     cl::desc("Trim cold functions until profile size is below specified "
2045f757f3fSDimitry Andric              "limit in bytes. This uses a heursitic and functions may be "
2055f757f3fSDimitry Andric              "excessively trimmed"));
2065f757f3fSDimitry Andric cl::opt<bool> GenPartialProfile(
2075f757f3fSDimitry Andric     "gen-partial-profile", cl::init(false), cl::Hidden,
2085f757f3fSDimitry Andric     cl::sub(MergeSubcommand),
2095f757f3fSDimitry Andric     cl::desc("Generate a partial profile (only meaningful for -extbinary)"));
2105f757f3fSDimitry Andric cl::opt<std::string> SupplInstrWithSample(
2115f757f3fSDimitry Andric     "supplement-instr-with-sample", cl::init(""), cl::Hidden,
2125f757f3fSDimitry Andric     cl::sub(MergeSubcommand),
2135f757f3fSDimitry Andric     cl::desc("Supplement an instr profile with sample profile, to correct "
2145f757f3fSDimitry Andric              "the profile unrepresentativeness issue. The sample "
2155f757f3fSDimitry Andric              "profile is the input of the flag. Output will be in instr "
2165f757f3fSDimitry Andric              "format (The flag only works with -instr)"));
2175f757f3fSDimitry Andric cl::opt<float> ZeroCounterThreshold(
2185f757f3fSDimitry Andric     "zero-counter-threshold", cl::init(0.7), cl::Hidden,
2195f757f3fSDimitry Andric     cl::sub(MergeSubcommand),
2205f757f3fSDimitry Andric     cl::desc("For the function which is cold in instr profile but hot in "
2215f757f3fSDimitry Andric              "sample profile, if the ratio of the number of zero counters "
2225f757f3fSDimitry Andric              "divided by the total number of counters is above the "
2235f757f3fSDimitry Andric              "threshold, the profile of the function will be regarded as "
2245f757f3fSDimitry Andric              "being harmful for performance and will be dropped."));
2255f757f3fSDimitry Andric cl::opt<unsigned> SupplMinSizeThreshold(
2265f757f3fSDimitry Andric     "suppl-min-size-threshold", cl::init(10), cl::Hidden,
2275f757f3fSDimitry Andric     cl::sub(MergeSubcommand),
2285f757f3fSDimitry Andric     cl::desc("If the size of a function is smaller than the threshold, "
2295f757f3fSDimitry Andric              "assume it can be inlined by PGO early inliner and it won't "
2305f757f3fSDimitry Andric              "be adjusted based on sample profile."));
2315f757f3fSDimitry Andric cl::opt<unsigned> InstrProfColdThreshold(
2325f757f3fSDimitry Andric     "instr-prof-cold-threshold", cl::init(0), cl::Hidden,
2335f757f3fSDimitry Andric     cl::sub(MergeSubcommand),
2345f757f3fSDimitry Andric     cl::desc("User specified cold threshold for instr profile which will "
2355f757f3fSDimitry Andric              "override the cold threshold got from profile summary. "));
2365f757f3fSDimitry Andric // WARNING: This reservoir size value is propagated to any input indexed
2375f757f3fSDimitry Andric // profiles for simplicity. Changing this value between invocations could
2385f757f3fSDimitry Andric // result in sample bias.
2395f757f3fSDimitry Andric cl::opt<uint64_t> TemporalProfTraceReservoirSize(
2405f757f3fSDimitry Andric     "temporal-profile-trace-reservoir-size", cl::init(100),
2415f757f3fSDimitry Andric     cl::sub(MergeSubcommand),
2425f757f3fSDimitry Andric     cl::desc("The maximum number of stored temporal profile traces (default: "
2435f757f3fSDimitry Andric              "100)"));
2445f757f3fSDimitry Andric cl::opt<uint64_t> TemporalProfMaxTraceLength(
2455f757f3fSDimitry Andric     "temporal-profile-max-trace-length", cl::init(10000),
2465f757f3fSDimitry Andric     cl::sub(MergeSubcommand),
2475f757f3fSDimitry Andric     cl::desc("The maximum length of a single temporal profile trace "
2485f757f3fSDimitry Andric              "(default: 10000)"));
2497a6dacacSDimitry Andric cl::opt<std::string> FuncNameNegativeFilter(
2507a6dacacSDimitry Andric     "no-function", cl::init(""),
2517a6dacacSDimitry Andric     cl::sub(MergeSubcommand),
2527a6dacacSDimitry Andric     cl::desc("Exclude functions matching the filter from the output."));
2535f757f3fSDimitry Andric 
2545f757f3fSDimitry Andric cl::opt<FailureMode>
2555f757f3fSDimitry Andric     FailMode("failure-mode", cl::init(failIfAnyAreInvalid),
2565f757f3fSDimitry Andric              cl::desc("Failure mode:"), cl::sub(MergeSubcommand),
2575f757f3fSDimitry Andric              cl::values(clEnumValN(warnOnly, "warn",
2585f757f3fSDimitry Andric                                    "Do not fail and just print warnings."),
2595f757f3fSDimitry Andric                         clEnumValN(failIfAnyAreInvalid, "any",
2605f757f3fSDimitry Andric                                    "Fail if any profile is invalid."),
2615f757f3fSDimitry Andric                         clEnumValN(failIfAllAreInvalid, "all",
2625f757f3fSDimitry Andric                                    "Fail only if all profiles are invalid.")));
2635f757f3fSDimitry Andric 
2645f757f3fSDimitry Andric cl::opt<bool> OutputSparse(
2655f757f3fSDimitry Andric     "sparse", cl::init(false), cl::sub(MergeSubcommand),
2665f757f3fSDimitry Andric     cl::desc("Generate a sparse profile (only meaningful for -instr)"));
2675f757f3fSDimitry Andric cl::opt<unsigned> NumThreads(
2685f757f3fSDimitry Andric     "num-threads", cl::init(0), cl::sub(MergeSubcommand),
2695f757f3fSDimitry Andric     cl::desc("Number of merge threads to use (default: autodetect)"));
2705f757f3fSDimitry Andric cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
2715f757f3fSDimitry Andric                       cl::aliasopt(NumThreads));
2725f757f3fSDimitry Andric 
2735f757f3fSDimitry Andric cl::opt<std::string> ProfileSymbolListFile(
2745f757f3fSDimitry Andric     "prof-sym-list", cl::init(""), cl::sub(MergeSubcommand),
2755f757f3fSDimitry Andric     cl::desc("Path to file containing the list of function symbols "
2765f757f3fSDimitry Andric              "used to populate profile symbol list"));
2775f757f3fSDimitry Andric 
2785f757f3fSDimitry Andric cl::opt<SampleProfileLayout> ProfileLayout(
2795f757f3fSDimitry Andric     "convert-sample-profile-layout",
2805f757f3fSDimitry Andric     cl::desc("Convert the generated profile to a profile with a new layout"),
2815f757f3fSDimitry Andric     cl::sub(MergeSubcommand), cl::init(SPL_None),
2825f757f3fSDimitry Andric     cl::values(
2835f757f3fSDimitry Andric         clEnumValN(SPL_Nest, "nest",
2845f757f3fSDimitry Andric                    "Nested profile, the input should be CS flat profile"),
2855f757f3fSDimitry Andric         clEnumValN(SPL_Flat, "flat",
2865f757f3fSDimitry Andric                    "Profile with nested inlinee flatten out")));
2875f757f3fSDimitry Andric 
2885f757f3fSDimitry Andric cl::opt<bool> DropProfileSymbolList(
2895f757f3fSDimitry Andric     "drop-profile-symbol-list", cl::init(false), cl::Hidden,
2905f757f3fSDimitry Andric     cl::sub(MergeSubcommand),
2915f757f3fSDimitry Andric     cl::desc("Drop the profile symbol list when merging AutoFDO profiles "
2925f757f3fSDimitry Andric              "(only meaningful for -sample)"));
2935f757f3fSDimitry Andric 
2945f757f3fSDimitry Andric // Options specific to overlap subcommand.
2955f757f3fSDimitry Andric cl::opt<std::string> BaseFilename(cl::Positional, cl::Required,
2965f757f3fSDimitry Andric                                   cl::desc("<base profile file>"),
2975f757f3fSDimitry Andric                                   cl::sub(OverlapSubcommand));
2985f757f3fSDimitry Andric cl::opt<std::string> TestFilename(cl::Positional, cl::Required,
2995f757f3fSDimitry Andric                                   cl::desc("<test profile file>"),
3005f757f3fSDimitry Andric                                   cl::sub(OverlapSubcommand));
3015f757f3fSDimitry Andric 
3025f757f3fSDimitry Andric cl::opt<unsigned long long> SimilarityCutoff(
3035f757f3fSDimitry Andric     "similarity-cutoff", cl::init(0),
3045f757f3fSDimitry Andric     cl::desc("For sample profiles, list function names (with calling context "
3055f757f3fSDimitry Andric              "for csspgo) for overlapped functions "
3065f757f3fSDimitry Andric              "with similarities below the cutoff (percentage times 10000)."),
3075f757f3fSDimitry Andric     cl::sub(OverlapSubcommand));
3085f757f3fSDimitry Andric 
3095f757f3fSDimitry Andric cl::opt<bool> IsCS(
3105f757f3fSDimitry Andric     "cs", cl::init(false),
3115f757f3fSDimitry Andric     cl::desc("For context sensitive PGO counts. Does not work with CSSPGO."),
3125f757f3fSDimitry Andric     cl::sub(OverlapSubcommand));
3135f757f3fSDimitry Andric 
3145f757f3fSDimitry Andric cl::opt<unsigned long long> OverlapValueCutoff(
3155f757f3fSDimitry Andric     "value-cutoff", cl::init(-1),
3165f757f3fSDimitry Andric     cl::desc(
3175f757f3fSDimitry Andric         "Function level overlap information for every function (with calling "
3185f757f3fSDimitry Andric         "context for csspgo) in test "
3195f757f3fSDimitry Andric         "profile with max count value greater then the parameter value"),
3205f757f3fSDimitry Andric     cl::sub(OverlapSubcommand));
3215f757f3fSDimitry Andric 
3225f757f3fSDimitry Andric // Options unique to show subcommand.
3235f757f3fSDimitry Andric cl::opt<bool> ShowCounts("counts", cl::init(false),
3245f757f3fSDimitry Andric                          cl::desc("Show counter values for shown functions"),
3255f757f3fSDimitry Andric                          cl::sub(ShowSubcommand));
3265f757f3fSDimitry Andric cl::opt<ShowFormat>
3275f757f3fSDimitry Andric     SFormat("show-format", cl::init(ShowFormat::Text),
3285f757f3fSDimitry Andric             cl::desc("Emit output in the selected format if supported"),
3295f757f3fSDimitry Andric             cl::sub(ShowSubcommand),
3305f757f3fSDimitry Andric             cl::values(clEnumValN(ShowFormat::Text, "text",
3315f757f3fSDimitry Andric                                   "emit normal text output (default)"),
3325f757f3fSDimitry Andric                        clEnumValN(ShowFormat::Json, "json", "emit JSON"),
3335f757f3fSDimitry Andric                        clEnumValN(ShowFormat::Yaml, "yaml", "emit YAML")));
3345f757f3fSDimitry Andric // TODO: Consider replacing this with `--show-format=text-encoding`.
3355f757f3fSDimitry Andric cl::opt<bool>
3365f757f3fSDimitry Andric     TextFormat("text", cl::init(false),
3375f757f3fSDimitry Andric                cl::desc("Show instr profile data in text dump format"),
3385f757f3fSDimitry Andric                cl::sub(ShowSubcommand));
3395f757f3fSDimitry Andric cl::opt<bool>
3405f757f3fSDimitry Andric     JsonFormat("json",
3415f757f3fSDimitry Andric                cl::desc("Show sample profile data in the JSON format "
3425f757f3fSDimitry Andric                         "(deprecated, please use --show-format=json)"),
3435f757f3fSDimitry Andric                cl::sub(ShowSubcommand));
3445f757f3fSDimitry Andric cl::opt<bool> ShowIndirectCallTargets(
3455f757f3fSDimitry Andric     "ic-targets", cl::init(false),
3465f757f3fSDimitry Andric     cl::desc("Show indirect call site target values for shown functions"),
3475f757f3fSDimitry Andric     cl::sub(ShowSubcommand));
3485f757f3fSDimitry Andric cl::opt<bool> ShowMemOPSizes(
3495f757f3fSDimitry Andric     "memop-sizes", cl::init(false),
3505f757f3fSDimitry Andric     cl::desc("Show the profiled sizes of the memory intrinsic calls "
3515f757f3fSDimitry Andric              "for shown functions"),
3525f757f3fSDimitry Andric     cl::sub(ShowSubcommand));
3535f757f3fSDimitry Andric cl::opt<bool> ShowDetailedSummary("detailed-summary", cl::init(false),
3545f757f3fSDimitry Andric                                   cl::desc("Show detailed profile summary"),
3555f757f3fSDimitry Andric                                   cl::sub(ShowSubcommand));
3565f757f3fSDimitry Andric cl::list<uint32_t> DetailedSummaryCutoffs(
3575f757f3fSDimitry Andric     cl::CommaSeparated, "detailed-summary-cutoffs",
3585f757f3fSDimitry Andric     cl::desc(
3595f757f3fSDimitry Andric         "Cutoff percentages (times 10000) for generating detailed summary"),
3605f757f3fSDimitry Andric     cl::value_desc("800000,901000,999999"), cl::sub(ShowSubcommand));
3615f757f3fSDimitry Andric cl::opt<bool>
3625f757f3fSDimitry Andric     ShowHotFuncList("hot-func-list", cl::init(false),
3635f757f3fSDimitry Andric                     cl::desc("Show profile summary of a list of hot functions"),
3645f757f3fSDimitry Andric                     cl::sub(ShowSubcommand));
3655f757f3fSDimitry Andric cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false),
3665f757f3fSDimitry Andric                                cl::desc("Details for each and every function"),
3675f757f3fSDimitry Andric                                cl::sub(ShowSubcommand));
3685f757f3fSDimitry Andric cl::opt<bool> ShowCS("showcs", cl::init(false),
3695f757f3fSDimitry Andric                      cl::desc("Show context sensitive counts"),
3705f757f3fSDimitry Andric                      cl::sub(ShowSubcommand));
3715f757f3fSDimitry Andric cl::opt<ProfileKinds> ShowProfileKind(
3725f757f3fSDimitry Andric     cl::desc("Profile kind supported by show:"), cl::sub(ShowSubcommand),
3735f757f3fSDimitry Andric     cl::init(instr),
3745f757f3fSDimitry Andric     cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
3755f757f3fSDimitry Andric                clEnumVal(sample, "Sample profile"),
3765f757f3fSDimitry Andric                clEnumVal(memory, "MemProf memory access profile")));
3775f757f3fSDimitry Andric cl::opt<uint32_t> TopNFunctions(
3785f757f3fSDimitry Andric     "topn", cl::init(0),
3795f757f3fSDimitry Andric     cl::desc("Show the list of functions with the largest internal counts"),
3805f757f3fSDimitry Andric     cl::sub(ShowSubcommand));
3815f757f3fSDimitry Andric cl::opt<uint32_t> ShowValueCutoff(
3825f757f3fSDimitry Andric     "value-cutoff", cl::init(0),
3835f757f3fSDimitry Andric     cl::desc("Set the count value cutoff. Functions with the maximum count "
3845f757f3fSDimitry Andric              "less than this value will not be printed out. (Default is 0)"),
3855f757f3fSDimitry Andric     cl::sub(ShowSubcommand));
3865f757f3fSDimitry Andric cl::opt<bool> OnlyListBelow(
3875f757f3fSDimitry Andric     "list-below-cutoff", cl::init(false),
3885f757f3fSDimitry Andric     cl::desc("Only output names of functions whose max count values are "
3895f757f3fSDimitry Andric              "below the cutoff value"),
3905f757f3fSDimitry Andric     cl::sub(ShowSubcommand));
3915f757f3fSDimitry Andric cl::opt<bool> ShowProfileSymbolList(
3925f757f3fSDimitry Andric     "show-prof-sym-list", cl::init(false),
3935f757f3fSDimitry Andric     cl::desc("Show profile symbol list if it exists in the profile. "),
3945f757f3fSDimitry Andric     cl::sub(ShowSubcommand));
3955f757f3fSDimitry Andric cl::opt<bool> ShowSectionInfoOnly(
3965f757f3fSDimitry Andric     "show-sec-info-only", cl::init(false),
3975f757f3fSDimitry Andric     cl::desc("Show the information of each section in the sample profile. "
3985f757f3fSDimitry Andric              "The flag is only usable when the sample profile is in "
3995f757f3fSDimitry Andric              "extbinary format"),
4005f757f3fSDimitry Andric     cl::sub(ShowSubcommand));
4015f757f3fSDimitry Andric cl::opt<bool> ShowBinaryIds("binary-ids", cl::init(false),
4025f757f3fSDimitry Andric                             cl::desc("Show binary ids in the profile. "),
4035f757f3fSDimitry Andric                             cl::sub(ShowSubcommand));
4045f757f3fSDimitry Andric cl::opt<bool> ShowTemporalProfTraces(
4055f757f3fSDimitry Andric     "temporal-profile-traces",
4065f757f3fSDimitry Andric     cl::desc("Show temporal profile traces in the profile."),
4075f757f3fSDimitry Andric     cl::sub(ShowSubcommand));
4085f757f3fSDimitry Andric 
4095f757f3fSDimitry Andric cl::opt<bool>
4105f757f3fSDimitry Andric     ShowCovered("covered", cl::init(false),
4115f757f3fSDimitry Andric                 cl::desc("Show only the functions that have been executed."),
4125f757f3fSDimitry Andric                 cl::sub(ShowSubcommand));
4135f757f3fSDimitry Andric 
4145f757f3fSDimitry Andric cl::opt<bool> ShowProfileVersion("profile-version", cl::init(false),
4155f757f3fSDimitry Andric                                  cl::desc("Show profile version. "),
4165f757f3fSDimitry Andric                                  cl::sub(ShowSubcommand));
4175f757f3fSDimitry Andric 
4185f757f3fSDimitry Andric // We use this string to indicate that there are
4195f757f3fSDimitry Andric // multiple static functions map to the same name.
4205f757f3fSDimitry Andric const std::string DuplicateNameStr = "----";
4215f757f3fSDimitry Andric 
warn(Twine Message,std::string Whence="",std::string Hint="")4220b57cec5SDimitry Andric static void warn(Twine Message, std::string Whence = "",
4230b57cec5SDimitry Andric                  std::string Hint = "") {
4240b57cec5SDimitry Andric   WithColor::warning();
4250b57cec5SDimitry Andric   if (!Whence.empty())
4260b57cec5SDimitry Andric     errs() << Whence << ": ";
4270b57cec5SDimitry Andric   errs() << Message << "\n";
4280b57cec5SDimitry Andric   if (!Hint.empty())
4290b57cec5SDimitry Andric     WithColor::note() << Hint << "\n";
4300b57cec5SDimitry Andric }
4310b57cec5SDimitry Andric 
warn(Error E,StringRef Whence="")432fe6060f1SDimitry Andric static void warn(Error E, StringRef Whence = "") {
433fe6060f1SDimitry Andric   if (E.isA<InstrProfError>()) {
434fe6060f1SDimitry Andric     handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
435fe6060f1SDimitry Andric       warn(IPE.message(), std::string(Whence), std::string(""));
436fe6060f1SDimitry Andric     });
437fe6060f1SDimitry Andric   }
438fe6060f1SDimitry Andric }
439fe6060f1SDimitry Andric 
exitWithError(Twine Message,std::string Whence="",std::string Hint="")4400b57cec5SDimitry Andric static void exitWithError(Twine Message, std::string Whence = "",
4410b57cec5SDimitry Andric                           std::string Hint = "") {
4420b57cec5SDimitry Andric   WithColor::error();
4430b57cec5SDimitry Andric   if (!Whence.empty())
4440b57cec5SDimitry Andric     errs() << Whence << ": ";
4450b57cec5SDimitry Andric   errs() << Message << "\n";
4460b57cec5SDimitry Andric   if (!Hint.empty())
4470b57cec5SDimitry Andric     WithColor::note() << Hint << "\n";
4480b57cec5SDimitry Andric   ::exit(1);
4490b57cec5SDimitry Andric }
4500b57cec5SDimitry Andric 
exitWithError(Error E,StringRef Whence="")4510b57cec5SDimitry Andric static void exitWithError(Error E, StringRef Whence = "") {
4520b57cec5SDimitry Andric   if (E.isA<InstrProfError>()) {
4530b57cec5SDimitry Andric     handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
4540b57cec5SDimitry Andric       instrprof_error instrError = IPE.get();
4550b57cec5SDimitry Andric       StringRef Hint = "";
4560b57cec5SDimitry Andric       if (instrError == instrprof_error::unrecognized_format) {
4574824e7fdSDimitry Andric         // Hint in case user missed specifying the profile type.
4584824e7fdSDimitry Andric         Hint = "Perhaps you forgot to use the --sample or --memory option?";
4590b57cec5SDimitry Andric       }
4605ffd83dbSDimitry Andric       exitWithError(IPE.message(), std::string(Whence), std::string(Hint));
4610b57cec5SDimitry Andric     });
46281ad6265SDimitry Andric     return;
4630b57cec5SDimitry Andric   }
4640b57cec5SDimitry Andric 
4655ffd83dbSDimitry Andric   exitWithError(toString(std::move(E)), std::string(Whence));
4660b57cec5SDimitry Andric }
4670b57cec5SDimitry Andric 
exitWithErrorCode(std::error_code EC,StringRef Whence="")4680b57cec5SDimitry Andric static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") {
4695ffd83dbSDimitry Andric   exitWithError(EC.message(), std::string(Whence));
4700b57cec5SDimitry Andric }
4710b57cec5SDimitry Andric 
warnOrExitGivenError(FailureMode FailMode,std::error_code EC,StringRef Whence="")4728bcb0991SDimitry Andric static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC,
4738bcb0991SDimitry Andric                                  StringRef Whence = "") {
4748bcb0991SDimitry Andric   if (FailMode == failIfAnyAreInvalid)
4758bcb0991SDimitry Andric     exitWithErrorCode(EC, Whence);
4768bcb0991SDimitry Andric   else
4775ffd83dbSDimitry Andric     warn(EC.message(), std::string(Whence));
4780b57cec5SDimitry Andric }
4790b57cec5SDimitry Andric 
handleMergeWriterError(Error E,StringRef WhenceFile="",StringRef WhenceFunction="",bool ShowHint=true)4800b57cec5SDimitry Andric static void handleMergeWriterError(Error E, StringRef WhenceFile = "",
4810b57cec5SDimitry Andric                                    StringRef WhenceFunction = "",
4820b57cec5SDimitry Andric                                    bool ShowHint = true) {
4830b57cec5SDimitry Andric   if (!WhenceFile.empty())
4840b57cec5SDimitry Andric     errs() << WhenceFile << ": ";
4850b57cec5SDimitry Andric   if (!WhenceFunction.empty())
4860b57cec5SDimitry Andric     errs() << WhenceFunction << ": ";
4870b57cec5SDimitry Andric 
4880b57cec5SDimitry Andric   auto IPE = instrprof_error::success;
4890b57cec5SDimitry Andric   E = handleErrors(std::move(E),
4900b57cec5SDimitry Andric                    [&IPE](std::unique_ptr<InstrProfError> E) -> Error {
4910b57cec5SDimitry Andric                      IPE = E->get();
4920b57cec5SDimitry Andric                      return Error(std::move(E));
4930b57cec5SDimitry Andric                    });
4940b57cec5SDimitry Andric   errs() << toString(std::move(E)) << "\n";
4950b57cec5SDimitry Andric 
4960b57cec5SDimitry Andric   if (ShowHint) {
4970b57cec5SDimitry Andric     StringRef Hint = "";
4980b57cec5SDimitry Andric     if (IPE != instrprof_error::success) {
4990b57cec5SDimitry Andric       switch (IPE) {
5000b57cec5SDimitry Andric       case instrprof_error::hash_mismatch:
5010b57cec5SDimitry Andric       case instrprof_error::count_mismatch:
5020b57cec5SDimitry Andric       case instrprof_error::value_site_count_mismatch:
5030b57cec5SDimitry Andric         Hint = "Make sure that all profile data to be merged is generated "
5040b57cec5SDimitry Andric                "from the same binary.";
5050b57cec5SDimitry Andric         break;
5060b57cec5SDimitry Andric       default:
5070b57cec5SDimitry Andric         break;
5080b57cec5SDimitry Andric       }
5090b57cec5SDimitry Andric     }
5100b57cec5SDimitry Andric 
5110b57cec5SDimitry Andric     if (!Hint.empty())
5120b57cec5SDimitry Andric       errs() << Hint << "\n";
5130b57cec5SDimitry Andric   }
5140b57cec5SDimitry Andric }
5150b57cec5SDimitry Andric 
5160b57cec5SDimitry Andric namespace {
5170b57cec5SDimitry Andric /// A remapper from original symbol names to new symbol names based on a file
5180b57cec5SDimitry Andric /// containing a list of mappings from old name to new name.
5190b57cec5SDimitry Andric class SymbolRemapper {
5200b57cec5SDimitry Andric   std::unique_ptr<MemoryBuffer> File;
5210b57cec5SDimitry Andric   DenseMap<StringRef, StringRef> RemappingTable;
5220b57cec5SDimitry Andric 
5230b57cec5SDimitry Andric public:
5240b57cec5SDimitry Andric   /// Build a SymbolRemapper from a file containing a list of old/new symbols.
create(StringRef InputFile)5250b57cec5SDimitry Andric   static std::unique_ptr<SymbolRemapper> create(StringRef InputFile) {
5260b57cec5SDimitry Andric     auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);
5270b57cec5SDimitry Andric     if (!BufOrError)
5280b57cec5SDimitry Andric       exitWithErrorCode(BufOrError.getError(), InputFile);
5290b57cec5SDimitry Andric 
5308bcb0991SDimitry Andric     auto Remapper = std::make_unique<SymbolRemapper>();
5310b57cec5SDimitry Andric     Remapper->File = std::move(BufOrError.get());
5320b57cec5SDimitry Andric 
5330b57cec5SDimitry Andric     for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#');
5340b57cec5SDimitry Andric          !LineIt.is_at_eof(); ++LineIt) {
5350b57cec5SDimitry Andric       std::pair<StringRef, StringRef> Parts = LineIt->split(' ');
5360b57cec5SDimitry Andric       if (Parts.first.empty() || Parts.second.empty() ||
5370b57cec5SDimitry Andric           Parts.second.count(' ')) {
5380b57cec5SDimitry Andric         exitWithError("unexpected line in remapping file",
5390b57cec5SDimitry Andric                       (InputFile + ":" + Twine(LineIt.line_number())).str(),
5400b57cec5SDimitry Andric                       "expected 'old_symbol new_symbol'");
5410b57cec5SDimitry Andric       }
5420b57cec5SDimitry Andric       Remapper->RemappingTable.insert(Parts);
5430b57cec5SDimitry Andric     }
5440b57cec5SDimitry Andric     return Remapper;
5450b57cec5SDimitry Andric   }
5460b57cec5SDimitry Andric 
5470b57cec5SDimitry Andric   /// Attempt to map the given old symbol into a new symbol.
5480b57cec5SDimitry Andric   ///
5490b57cec5SDimitry Andric   /// \return The new symbol, or \p Name if no such symbol was found.
operator ()(StringRef Name)5500b57cec5SDimitry Andric   StringRef operator()(StringRef Name) {
5510b57cec5SDimitry Andric     StringRef New = RemappingTable.lookup(Name);
5520b57cec5SDimitry Andric     return New.empty() ? Name : New;
5530b57cec5SDimitry Andric   }
5545f757f3fSDimitry Andric 
operator ()(FunctionId Name)5555f757f3fSDimitry Andric   FunctionId operator()(FunctionId Name) {
5565f757f3fSDimitry Andric     // MD5 name cannot be remapped.
5575f757f3fSDimitry Andric     if (!Name.isStringRef())
5585f757f3fSDimitry Andric       return Name;
5595f757f3fSDimitry Andric     StringRef New = RemappingTable.lookup(Name.stringRef());
5605f757f3fSDimitry Andric     return New.empty() ? Name : FunctionId(New);
5615f757f3fSDimitry Andric   }
5620b57cec5SDimitry Andric };
5630b57cec5SDimitry Andric }
5640b57cec5SDimitry Andric 
5650b57cec5SDimitry Andric struct WeightedFile {
5660b57cec5SDimitry Andric   std::string Filename;
5670b57cec5SDimitry Andric   uint64_t Weight;
5680b57cec5SDimitry Andric };
5690b57cec5SDimitry Andric typedef SmallVector<WeightedFile, 5> WeightedFileVector;
5700b57cec5SDimitry Andric 
5710b57cec5SDimitry Andric /// Keep track of merged data and reported errors.
5720b57cec5SDimitry Andric struct WriterContext {
5730b57cec5SDimitry Andric   std::mutex Lock;
5740b57cec5SDimitry Andric   InstrProfWriter Writer;
5758bcb0991SDimitry Andric   std::vector<std::pair<Error, std::string>> Errors;
5760b57cec5SDimitry Andric   std::mutex &ErrLock;
5770b57cec5SDimitry Andric   SmallSet<instrprof_error, 4> &WriterErrorCodes;
5780b57cec5SDimitry Andric 
WriterContextWriterContext5790b57cec5SDimitry Andric   WriterContext(bool IsSparse, std::mutex &ErrLock,
58006c3fb27SDimitry Andric                 SmallSet<instrprof_error, 4> &WriterErrorCodes,
58106c3fb27SDimitry Andric                 uint64_t ReservoirSize = 0, uint64_t MaxTraceLength = 0)
58206c3fb27SDimitry Andric       : Writer(IsSparse, ReservoirSize, MaxTraceLength), ErrLock(ErrLock),
58306c3fb27SDimitry Andric         WriterErrorCodes(WriterErrorCodes) {}
5840b57cec5SDimitry Andric };
5850b57cec5SDimitry Andric 
5860b57cec5SDimitry Andric /// Computer the overlap b/w profile BaseFilename and TestFileName,
5870b57cec5SDimitry Andric /// 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)5880b57cec5SDimitry Andric static void overlapInput(const std::string &BaseFilename,
5890b57cec5SDimitry Andric                          const std::string &TestFilename, WriterContext *WC,
5900b57cec5SDimitry Andric                          OverlapStats &Overlap,
5910b57cec5SDimitry Andric                          const OverlapFuncFilters &FuncFilter,
5920b57cec5SDimitry Andric                          raw_fd_ostream &OS, bool IsCS) {
59306c3fb27SDimitry Andric   auto FS = vfs::getRealFileSystem();
59406c3fb27SDimitry Andric   auto ReaderOrErr = InstrProfReader::create(TestFilename, *FS);
5950b57cec5SDimitry Andric   if (Error E = ReaderOrErr.takeError()) {
5960b57cec5SDimitry Andric     // Skip the empty profiles by returning sliently.
59706c3fb27SDimitry Andric     auto [ErrorCode, Msg] = InstrProfError::take(std::move(E));
59806c3fb27SDimitry Andric     if (ErrorCode != instrprof_error::empty_raw_profile)
59906c3fb27SDimitry Andric       WC->Errors.emplace_back(make_error<InstrProfError>(ErrorCode, Msg),
60006c3fb27SDimitry Andric                               TestFilename);
6010b57cec5SDimitry Andric     return;
6020b57cec5SDimitry Andric   }
6030b57cec5SDimitry Andric 
6040b57cec5SDimitry Andric   auto Reader = std::move(ReaderOrErr.get());
6050b57cec5SDimitry Andric   for (auto &I : *Reader) {
6060b57cec5SDimitry Andric     OverlapStats FuncOverlap(OverlapStats::FunctionLevel);
6070b57cec5SDimitry Andric     FuncOverlap.setFuncInfo(I.Name, I.Hash);
6080b57cec5SDimitry Andric 
6090b57cec5SDimitry Andric     WC->Writer.overlapRecord(std::move(I), Overlap, FuncOverlap, FuncFilter);
6100b57cec5SDimitry Andric     FuncOverlap.dump(OS);
6110b57cec5SDimitry Andric   }
6120b57cec5SDimitry Andric }
6130b57cec5SDimitry Andric 
6140b57cec5SDimitry Andric /// Load an input into a writer context.
loadInput(const WeightedFile & Input,SymbolRemapper * Remapper,const InstrProfCorrelator * Correlator,const StringRef ProfiledBinary,WriterContext * WC)6150b57cec5SDimitry Andric static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper,
6160eae32dcSDimitry Andric                       const InstrProfCorrelator *Correlator,
61781ad6265SDimitry Andric                       const StringRef ProfiledBinary, WriterContext *WC) {
6180b57cec5SDimitry Andric   std::unique_lock<std::mutex> CtxGuard{WC->Lock};
6190b57cec5SDimitry Andric 
6200b57cec5SDimitry Andric   // Copy the filename, because llvm::ThreadPool copied the input "const
6210b57cec5SDimitry Andric   // WeightedFile &" by value, making a reference to the filename within it
6220b57cec5SDimitry Andric   // invalid outside of this packaged task.
6238bcb0991SDimitry Andric   std::string Filename = Input.Filename;
6240b57cec5SDimitry Andric 
62581ad6265SDimitry Andric   using ::llvm::memprof::RawMemProfReader;
62681ad6265SDimitry Andric   if (RawMemProfReader::hasFormat(Input.Filename)) {
62781ad6265SDimitry Andric     auto ReaderOrErr = RawMemProfReader::create(Input.Filename, ProfiledBinary);
62881ad6265SDimitry Andric     if (!ReaderOrErr) {
62981ad6265SDimitry Andric       exitWithError(ReaderOrErr.takeError(), Input.Filename);
63081ad6265SDimitry Andric     }
63181ad6265SDimitry Andric     std::unique_ptr<RawMemProfReader> Reader = std::move(ReaderOrErr.get());
63281ad6265SDimitry Andric     // Check if the profile types can be merged, e.g. clang frontend profiles
63381ad6265SDimitry Andric     // should not be merged with memprof profiles.
63481ad6265SDimitry Andric     if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) {
63581ad6265SDimitry Andric       consumeError(std::move(E));
63681ad6265SDimitry Andric       WC->Errors.emplace_back(
63781ad6265SDimitry Andric           make_error<StringError>(
63881ad6265SDimitry Andric               "Cannot merge MemProf profile with Clang generated profile.",
63981ad6265SDimitry Andric               std::error_code()),
64081ad6265SDimitry Andric           Filename);
64181ad6265SDimitry Andric       return;
64281ad6265SDimitry Andric     }
64381ad6265SDimitry Andric 
64481ad6265SDimitry Andric     auto MemProfError = [&](Error E) {
64506c3fb27SDimitry Andric       auto [ErrorCode, Msg] = InstrProfError::take(std::move(E));
64606c3fb27SDimitry Andric       WC->Errors.emplace_back(make_error<InstrProfError>(ErrorCode, Msg),
64706c3fb27SDimitry Andric                               Filename);
64881ad6265SDimitry Andric     };
64981ad6265SDimitry Andric 
65081ad6265SDimitry Andric     // Add the frame mappings into the writer context.
65181ad6265SDimitry Andric     const auto &IdToFrame = Reader->getFrameMapping();
65281ad6265SDimitry Andric     for (const auto &I : IdToFrame) {
65381ad6265SDimitry Andric       bool Succeeded = WC->Writer.addMemProfFrame(
65481ad6265SDimitry Andric           /*Id=*/I.first, /*Frame=*/I.getSecond(), MemProfError);
65581ad6265SDimitry Andric       // If we weren't able to add the frame mappings then it doesn't make sense
65681ad6265SDimitry Andric       // to try to add the records from this profile.
65781ad6265SDimitry Andric       if (!Succeeded)
65881ad6265SDimitry Andric         return;
65981ad6265SDimitry Andric     }
66081ad6265SDimitry Andric     const auto &FunctionProfileData = Reader->getProfileData();
66181ad6265SDimitry Andric     // Add the memprof records into the writer context.
66281ad6265SDimitry Andric     for (const auto &I : FunctionProfileData) {
66381ad6265SDimitry Andric       WC->Writer.addMemProfRecord(/*Id=*/I.first, /*Record=*/I.second);
66481ad6265SDimitry Andric     }
66581ad6265SDimitry Andric     return;
66681ad6265SDimitry Andric   }
66781ad6265SDimitry Andric 
66806c3fb27SDimitry Andric   auto FS = vfs::getRealFileSystem();
6695f757f3fSDimitry Andric   // TODO: This only saves the first non-fatal error from InstrProfReader, and
6705f757f3fSDimitry Andric   // then added to WriterContext::Errors. However, this is not extensible, if
6715f757f3fSDimitry Andric   // we have more non-fatal errors from InstrProfReader in the future. How
6725f757f3fSDimitry Andric   // should this interact with different -failure-mode?
6735f757f3fSDimitry Andric   std::optional<std::pair<Error, std::string>> ReaderWarning;
6745f757f3fSDimitry Andric   auto Warn = [&](Error E) {
6755f757f3fSDimitry Andric     if (ReaderWarning) {
6765f757f3fSDimitry Andric       consumeError(std::move(E));
6775f757f3fSDimitry Andric       return;
6785f757f3fSDimitry Andric     }
6795f757f3fSDimitry Andric     // Only show the first time an error occurs in this file.
6805f757f3fSDimitry Andric     auto [ErrCode, Msg] = InstrProfError::take(std::move(E));
6815f757f3fSDimitry Andric     ReaderWarning = {make_error<InstrProfError>(ErrCode, Msg), Filename};
6825f757f3fSDimitry Andric   };
6835f757f3fSDimitry Andric   auto ReaderOrErr =
6845f757f3fSDimitry Andric       InstrProfReader::create(Input.Filename, *FS, Correlator, Warn);
6850b57cec5SDimitry Andric   if (Error E = ReaderOrErr.takeError()) {
68606c3fb27SDimitry Andric     // Skip the empty profiles by returning silently.
68706c3fb27SDimitry Andric     auto [ErrCode, Msg] = InstrProfError::take(std::move(E));
68806c3fb27SDimitry Andric     if (ErrCode != instrprof_error::empty_raw_profile)
68906c3fb27SDimitry Andric       WC->Errors.emplace_back(make_error<InstrProfError>(ErrCode, Msg),
69006c3fb27SDimitry Andric                               Filename);
6910b57cec5SDimitry Andric     return;
6920b57cec5SDimitry Andric   }
6930b57cec5SDimitry Andric 
6940b57cec5SDimitry Andric   auto Reader = std::move(ReaderOrErr.get());
6951fd87a68SDimitry Andric   if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) {
696fe6060f1SDimitry Andric     consumeError(std::move(E));
6978bcb0991SDimitry Andric     WC->Errors.emplace_back(
6988bcb0991SDimitry Andric         make_error<StringError>(
6990b57cec5SDimitry Andric             "Merge IR generated profile with Clang generated profile.",
7008bcb0991SDimitry Andric             std::error_code()),
7018bcb0991SDimitry Andric         Filename);
7020b57cec5SDimitry Andric     return;
7030b57cec5SDimitry Andric   }
7040b57cec5SDimitry Andric 
7050b57cec5SDimitry Andric   for (auto &I : *Reader) {
7060b57cec5SDimitry Andric     if (Remapper)
7070b57cec5SDimitry Andric       I.Name = (*Remapper)(I.Name);
7080b57cec5SDimitry Andric     const StringRef FuncName = I.Name;
7090b57cec5SDimitry Andric     bool Reported = false;
7100b57cec5SDimitry Andric     WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) {
7110b57cec5SDimitry Andric       if (Reported) {
7120b57cec5SDimitry Andric         consumeError(std::move(E));
7130b57cec5SDimitry Andric         return;
7140b57cec5SDimitry Andric       }
7150b57cec5SDimitry Andric       Reported = true;
7160b57cec5SDimitry Andric       // Only show hint the first time an error occurs.
71706c3fb27SDimitry Andric       auto [ErrCode, Msg] = InstrProfError::take(std::move(E));
7180b57cec5SDimitry Andric       std::unique_lock<std::mutex> ErrGuard{WC->ErrLock};
71906c3fb27SDimitry Andric       bool firstTime = WC->WriterErrorCodes.insert(ErrCode).second;
72006c3fb27SDimitry Andric       handleMergeWriterError(make_error<InstrProfError>(ErrCode, Msg),
72106c3fb27SDimitry Andric                              Input.Filename, FuncName, firstTime);
7220b57cec5SDimitry Andric     });
7230b57cec5SDimitry Andric   }
724bdd1243dSDimitry Andric 
72506c3fb27SDimitry Andric   if (Reader->hasTemporalProfile()) {
72606c3fb27SDimitry Andric     auto &Traces = Reader->getTemporalProfTraces(Input.Weight);
72706c3fb27SDimitry Andric     if (!Traces.empty())
72806c3fb27SDimitry Andric       WC->Writer.addTemporalProfileTraces(
72906c3fb27SDimitry Andric           Traces, Reader->getTemporalProfTraceStreamSize());
73006c3fb27SDimitry Andric   }
731bdd1243dSDimitry Andric   if (Reader->hasError()) {
7325f757f3fSDimitry Andric     if (Error E = Reader->getError()) {
7338bcb0991SDimitry Andric       WC->Errors.emplace_back(std::move(E), Filename);
7345f757f3fSDimitry Andric       return;
7355f757f3fSDimitry Andric     }
7360b57cec5SDimitry Andric   }
7370b57cec5SDimitry Andric 
738bdd1243dSDimitry Andric   std::vector<llvm::object::BuildID> BinaryIds;
7395f757f3fSDimitry Andric   if (Error E = Reader->readBinaryIds(BinaryIds)) {
740bdd1243dSDimitry Andric     WC->Errors.emplace_back(std::move(E), Filename);
7415f757f3fSDimitry Andric     return;
7425f757f3fSDimitry Andric   }
743bdd1243dSDimitry Andric   WC->Writer.addBinaryIds(BinaryIds);
7445f757f3fSDimitry Andric 
7455f757f3fSDimitry Andric   if (ReaderWarning) {
7465f757f3fSDimitry Andric     WC->Errors.emplace_back(std::move(ReaderWarning->first),
7475f757f3fSDimitry Andric                             ReaderWarning->second);
7485f757f3fSDimitry Andric   }
749bdd1243dSDimitry Andric }
750bdd1243dSDimitry Andric 
7510b57cec5SDimitry Andric /// Merge the \p Src writer context into \p Dst.
mergeWriterContexts(WriterContext * Dst,WriterContext * Src)7520b57cec5SDimitry Andric static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) {
7538bcb0991SDimitry Andric   for (auto &ErrorPair : Src->Errors)
7548bcb0991SDimitry Andric     Dst->Errors.push_back(std::move(ErrorPair));
7558bcb0991SDimitry Andric   Src->Errors.clear();
7560b57cec5SDimitry Andric 
757bdd1243dSDimitry Andric   if (Error E = Dst->Writer.mergeProfileKind(Src->Writer.getProfileKind()))
758bdd1243dSDimitry Andric     exitWithError(std::move(E));
759bdd1243dSDimitry Andric 
7600b57cec5SDimitry Andric   Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) {
76106c3fb27SDimitry Andric     auto [ErrorCode, Msg] = InstrProfError::take(std::move(E));
7628bcb0991SDimitry Andric     std::unique_lock<std::mutex> ErrGuard{Dst->ErrLock};
76306c3fb27SDimitry Andric     bool firstTime = Dst->WriterErrorCodes.insert(ErrorCode).second;
7648bcb0991SDimitry Andric     if (firstTime)
76506c3fb27SDimitry Andric       warn(toString(make_error<InstrProfError>(ErrorCode, Msg)));
7660b57cec5SDimitry Andric   });
7670b57cec5SDimitry Andric }
7680b57cec5SDimitry Andric 
7697a6dacacSDimitry Andric static StringRef
getFuncName(const StringMap<InstrProfWriter::ProfilingData>::value_type & Val)7707a6dacacSDimitry Andric getFuncName(const StringMap<InstrProfWriter::ProfilingData>::value_type &Val) {
7717a6dacacSDimitry Andric   return Val.first();
7727a6dacacSDimitry Andric }
7737a6dacacSDimitry Andric 
7747a6dacacSDimitry Andric static std::string
getFuncName(const SampleProfileMap::value_type & Val)7757a6dacacSDimitry Andric getFuncName(const SampleProfileMap::value_type &Val) {
7767a6dacacSDimitry Andric   return Val.second.getContext().toString();
7777a6dacacSDimitry Andric }
7787a6dacacSDimitry Andric 
7797a6dacacSDimitry Andric template <typename T>
filterFunctions(T & ProfileMap)7807a6dacacSDimitry Andric static void filterFunctions(T &ProfileMap) {
7817a6dacacSDimitry Andric   bool hasFilter = !FuncNameFilter.empty();
7827a6dacacSDimitry Andric   bool hasNegativeFilter = !FuncNameNegativeFilter.empty();
7837a6dacacSDimitry Andric   if (!hasFilter && !hasNegativeFilter)
7847a6dacacSDimitry Andric     return;
7857a6dacacSDimitry Andric 
7867a6dacacSDimitry Andric   // If filter starts with '?' it is MSVC mangled name, not a regex.
7877a6dacacSDimitry Andric   llvm::Regex ProbablyMSVCMangledName("[?@$_0-9A-Za-z]+");
7887a6dacacSDimitry Andric   if (hasFilter && FuncNameFilter[0] == '?' &&
7897a6dacacSDimitry Andric       ProbablyMSVCMangledName.match(FuncNameFilter))
7907a6dacacSDimitry Andric     FuncNameFilter = llvm::Regex::escape(FuncNameFilter);
7917a6dacacSDimitry Andric   if (hasNegativeFilter && FuncNameNegativeFilter[0] == '?' &&
7927a6dacacSDimitry Andric       ProbablyMSVCMangledName.match(FuncNameNegativeFilter))
7937a6dacacSDimitry Andric     FuncNameNegativeFilter = llvm::Regex::escape(FuncNameNegativeFilter);
7947a6dacacSDimitry Andric 
7957a6dacacSDimitry Andric   size_t Count = ProfileMap.size();
7967a6dacacSDimitry Andric   llvm::Regex Pattern(FuncNameFilter);
7977a6dacacSDimitry Andric   llvm::Regex NegativePattern(FuncNameNegativeFilter);
7987a6dacacSDimitry Andric   std::string Error;
7997a6dacacSDimitry Andric   if (hasFilter && !Pattern.isValid(Error))
8007a6dacacSDimitry Andric     exitWithError(Error);
8017a6dacacSDimitry Andric   if (hasNegativeFilter && !NegativePattern.isValid(Error))
8027a6dacacSDimitry Andric     exitWithError(Error);
8037a6dacacSDimitry Andric 
8047a6dacacSDimitry Andric   // Handle MD5 profile, so it is still able to match using the original name.
8057a6dacacSDimitry Andric   std::string MD5Name = std::to_string(llvm::MD5Hash(FuncNameFilter));
8067a6dacacSDimitry Andric   std::string NegativeMD5Name =
8077a6dacacSDimitry Andric       std::to_string(llvm::MD5Hash(FuncNameNegativeFilter));
8087a6dacacSDimitry Andric 
8097a6dacacSDimitry Andric   for (auto I = ProfileMap.begin(); I != ProfileMap.end();) {
8107a6dacacSDimitry Andric     auto Tmp = I++;
8117a6dacacSDimitry Andric     const auto &FuncName = getFuncName(*Tmp);
8127a6dacacSDimitry Andric     // Negative filter has higher precedence than positive filter.
8137a6dacacSDimitry Andric     if ((hasNegativeFilter &&
8147a6dacacSDimitry Andric          (NegativePattern.match(FuncName) ||
8157a6dacacSDimitry Andric           (FunctionSamples::UseMD5 && NegativeMD5Name == FuncName))) ||
8167a6dacacSDimitry Andric         (hasFilter && !(Pattern.match(FuncName) ||
8177a6dacacSDimitry Andric                         (FunctionSamples::UseMD5 && MD5Name == FuncName))))
8187a6dacacSDimitry Andric       ProfileMap.erase(Tmp);
8197a6dacacSDimitry Andric   }
8207a6dacacSDimitry Andric 
8217a6dacacSDimitry Andric   llvm::dbgs() << Count - ProfileMap.size() << " of " << Count << " functions "
8227a6dacacSDimitry Andric                << "in the original profile are filtered.\n";
8237a6dacacSDimitry Andric }
8247a6dacacSDimitry Andric 
writeInstrProfile(StringRef OutputFilename,ProfileFormat OutputFormat,InstrProfWriter & Writer)8255ffd83dbSDimitry Andric static void writeInstrProfile(StringRef OutputFilename,
8265ffd83dbSDimitry Andric                               ProfileFormat OutputFormat,
8275ffd83dbSDimitry Andric                               InstrProfWriter &Writer) {
8285ffd83dbSDimitry Andric   std::error_code EC;
829e8d8bef9SDimitry Andric   raw_fd_ostream Output(OutputFilename.data(), EC,
830fe6060f1SDimitry Andric                         OutputFormat == PF_Text ? sys::fs::OF_TextWithCRLF
831e8d8bef9SDimitry Andric                                                 : sys::fs::OF_None);
8325ffd83dbSDimitry Andric   if (EC)
8335ffd83dbSDimitry Andric     exitWithErrorCode(EC, OutputFilename);
8345ffd83dbSDimitry Andric 
8355ffd83dbSDimitry Andric   if (OutputFormat == PF_Text) {
8365ffd83dbSDimitry Andric     if (Error E = Writer.writeText(Output))
837fe6060f1SDimitry Andric       warn(std::move(E));
8385ffd83dbSDimitry Andric   } else {
839fe6060f1SDimitry Andric     if (Output.is_displayed())
840fe6060f1SDimitry Andric       exitWithError("cannot write a non-text format profile to the terminal");
841fe6060f1SDimitry Andric     if (Error E = Writer.write(Output))
842fe6060f1SDimitry Andric       warn(std::move(E));
8435ffd83dbSDimitry Andric   }
8445ffd83dbSDimitry Andric }
8455ffd83dbSDimitry Andric 
mergeInstrProfile(const WeightedFileVector & Inputs,SymbolRemapper * Remapper,int MaxDbgCorrelationWarnings,const StringRef ProfiledBinary)8465f757f3fSDimitry Andric static void mergeInstrProfile(const WeightedFileVector &Inputs,
8475f757f3fSDimitry Andric                               SymbolRemapper *Remapper,
8485f757f3fSDimitry Andric                               int MaxDbgCorrelationWarnings,
84981ad6265SDimitry Andric                               const StringRef ProfiledBinary) {
8505f757f3fSDimitry Andric   const uint64_t TraceReservoirSize = TemporalProfTraceReservoirSize.getValue();
8515f757f3fSDimitry Andric   const uint64_t MaxTraceLength = TemporalProfMaxTraceLength.getValue();
85206c3fb27SDimitry Andric   if (OutputFormat == PF_Compact_Binary)
85306c3fb27SDimitry Andric     exitWithError("Compact Binary is deprecated");
85406c3fb27SDimitry Andric   if (OutputFormat != PF_Binary && OutputFormat != PF_Ext_Binary &&
85506c3fb27SDimitry Andric       OutputFormat != PF_Text)
856fe6060f1SDimitry Andric     exitWithError("unknown format is specified");
8570b57cec5SDimitry Andric 
8585f757f3fSDimitry Andric   // TODO: Maybe we should support correlation with mixture of different
8595f757f3fSDimitry Andric   // correlation modes(w/wo debug-info/object correlation).
8605f757f3fSDimitry Andric   if (!DebugInfoFilename.empty() && !BinaryFilename.empty())
8615f757f3fSDimitry Andric     exitWithError("Expected only one of -debug-info, -binary-file");
8625f757f3fSDimitry Andric   std::string CorrelateFilename;
8635f757f3fSDimitry Andric   ProfCorrelatorKind CorrelateKind = ProfCorrelatorKind::NONE;
8640eae32dcSDimitry Andric   if (!DebugInfoFilename.empty()) {
8655f757f3fSDimitry Andric     CorrelateFilename = DebugInfoFilename;
8665f757f3fSDimitry Andric     CorrelateKind = ProfCorrelatorKind::DEBUG_INFO;
8675f757f3fSDimitry Andric   } else if (!BinaryFilename.empty()) {
8685f757f3fSDimitry Andric     CorrelateFilename = BinaryFilename;
8695f757f3fSDimitry Andric     CorrelateKind = ProfCorrelatorKind::BINARY;
8705f757f3fSDimitry Andric   }
8715f757f3fSDimitry Andric 
8725f757f3fSDimitry Andric   std::unique_ptr<InstrProfCorrelator> Correlator;
8735f757f3fSDimitry Andric   if (CorrelateKind != InstrProfCorrelator::NONE) {
8745f757f3fSDimitry Andric     if (auto Err = InstrProfCorrelator::get(CorrelateFilename, CorrelateKind)
8755f757f3fSDimitry Andric                        .moveInto(Correlator))
8765f757f3fSDimitry Andric       exitWithError(std::move(Err), CorrelateFilename);
8775f757f3fSDimitry Andric     if (auto Err = Correlator->correlateProfileData(MaxDbgCorrelationWarnings))
8785f757f3fSDimitry Andric       exitWithError(std::move(Err), CorrelateFilename);
8790eae32dcSDimitry Andric   }
8800eae32dcSDimitry Andric 
8810b57cec5SDimitry Andric   std::mutex ErrorLock;
8820b57cec5SDimitry Andric   SmallSet<instrprof_error, 4> WriterErrorCodes;
8830b57cec5SDimitry Andric 
8840b57cec5SDimitry Andric   // If NumThreads is not specified, auto-detect a good default.
8850b57cec5SDimitry Andric   if (NumThreads == 0)
8865ffd83dbSDimitry Andric     NumThreads = std::min(hardware_concurrency().compute_thread_count(),
8875ffd83dbSDimitry Andric                           unsigned((Inputs.size() + 1) / 2));
8880b57cec5SDimitry Andric 
8890b57cec5SDimitry Andric   // Initialize the writer contexts.
8900b57cec5SDimitry Andric   SmallVector<std::unique_ptr<WriterContext>, 4> Contexts;
8910b57cec5SDimitry Andric   for (unsigned I = 0; I < NumThreads; ++I)
8928bcb0991SDimitry Andric     Contexts.emplace_back(std::make_unique<WriterContext>(
89306c3fb27SDimitry Andric         OutputSparse, ErrorLock, WriterErrorCodes, TraceReservoirSize,
89406c3fb27SDimitry Andric         MaxTraceLength));
8950b57cec5SDimitry Andric 
8960b57cec5SDimitry Andric   if (NumThreads == 1) {
8970b57cec5SDimitry Andric     for (const auto &Input : Inputs)
89881ad6265SDimitry Andric       loadInput(Input, Remapper, Correlator.get(), ProfiledBinary,
89981ad6265SDimitry Andric                 Contexts[0].get());
9000b57cec5SDimitry Andric   } else {
9015ffd83dbSDimitry Andric     ThreadPool Pool(hardware_concurrency(NumThreads));
9020b57cec5SDimitry Andric 
9030b57cec5SDimitry Andric     // Load the inputs in parallel (N/NumThreads serial steps).
9040b57cec5SDimitry Andric     unsigned Ctx = 0;
9050b57cec5SDimitry Andric     for (const auto &Input : Inputs) {
90681ad6265SDimitry Andric       Pool.async(loadInput, Input, Remapper, Correlator.get(), ProfiledBinary,
9070eae32dcSDimitry Andric                  Contexts[Ctx].get());
9080b57cec5SDimitry Andric       Ctx = (Ctx + 1) % NumThreads;
9090b57cec5SDimitry Andric     }
9100b57cec5SDimitry Andric     Pool.wait();
9110b57cec5SDimitry Andric 
9120b57cec5SDimitry Andric     // Merge the writer contexts together (~ lg(NumThreads) serial steps).
9130b57cec5SDimitry Andric     unsigned Mid = Contexts.size() / 2;
9140b57cec5SDimitry Andric     unsigned End = Contexts.size();
9150b57cec5SDimitry Andric     assert(Mid > 0 && "Expected more than one context");
9160b57cec5SDimitry Andric     do {
9170b57cec5SDimitry Andric       for (unsigned I = 0; I < Mid; ++I)
9180b57cec5SDimitry Andric         Pool.async(mergeWriterContexts, Contexts[I].get(),
9190b57cec5SDimitry Andric                    Contexts[I + Mid].get());
9200b57cec5SDimitry Andric       Pool.wait();
9210b57cec5SDimitry Andric       if (End & 1) {
9220b57cec5SDimitry Andric         Pool.async(mergeWriterContexts, Contexts[0].get(),
9230b57cec5SDimitry Andric                    Contexts[End - 1].get());
9240b57cec5SDimitry Andric         Pool.wait();
9250b57cec5SDimitry Andric       }
9260b57cec5SDimitry Andric       End = Mid;
9270b57cec5SDimitry Andric       Mid /= 2;
9280b57cec5SDimitry Andric     } while (Mid > 0);
9290b57cec5SDimitry Andric   }
9300b57cec5SDimitry Andric 
9318bcb0991SDimitry Andric   // Handle deferred errors encountered during merging. If the number of errors
9328bcb0991SDimitry Andric   // is equal to the number of inputs the merge failed.
9338bcb0991SDimitry Andric   unsigned NumErrors = 0;
9340b57cec5SDimitry Andric   for (std::unique_ptr<WriterContext> &WC : Contexts) {
9358bcb0991SDimitry Andric     for (auto &ErrorPair : WC->Errors) {
9368bcb0991SDimitry Andric       ++NumErrors;
9378bcb0991SDimitry Andric       warn(toString(std::move(ErrorPair.first)), ErrorPair.second);
9380b57cec5SDimitry Andric     }
9398bcb0991SDimitry Andric   }
9405f757f3fSDimitry Andric   if ((NumErrors == Inputs.size() && FailMode == failIfAllAreInvalid) ||
9418bcb0991SDimitry Andric       (NumErrors > 0 && FailMode == failIfAnyAreInvalid))
942fe6060f1SDimitry Andric     exitWithError("no profile can be merged");
9430b57cec5SDimitry Andric 
9447a6dacacSDimitry Andric   filterFunctions(Contexts[0]->Writer.getProfileData());
9457a6dacacSDimitry Andric 
9465ffd83dbSDimitry Andric   writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer);
9470b57cec5SDimitry Andric }
9480b57cec5SDimitry Andric 
949e8d8bef9SDimitry Andric /// The profile entry for a function in instrumentation profile.
950e8d8bef9SDimitry Andric struct InstrProfileEntry {
951e8d8bef9SDimitry Andric   uint64_t MaxCount = 0;
952bdd1243dSDimitry Andric   uint64_t NumEdgeCounters = 0;
953e8d8bef9SDimitry Andric   float ZeroCounterRatio = 0.0;
954e8d8bef9SDimitry Andric   InstrProfRecord *ProfRecord;
955e8d8bef9SDimitry Andric   InstrProfileEntry(InstrProfRecord *Record);
956e8d8bef9SDimitry Andric   InstrProfileEntry() = default;
957e8d8bef9SDimitry Andric };
958e8d8bef9SDimitry Andric 
InstrProfileEntry(InstrProfRecord * Record)959e8d8bef9SDimitry Andric InstrProfileEntry::InstrProfileEntry(InstrProfRecord *Record) {
960e8d8bef9SDimitry Andric   ProfRecord = Record;
961e8d8bef9SDimitry Andric   uint64_t CntNum = Record->Counts.size();
962e8d8bef9SDimitry Andric   uint64_t ZeroCntNum = 0;
963e8d8bef9SDimitry Andric   for (size_t I = 0; I < CntNum; ++I) {
964e8d8bef9SDimitry Andric     MaxCount = std::max(MaxCount, Record->Counts[I]);
965e8d8bef9SDimitry Andric     ZeroCntNum += !Record->Counts[I];
966e8d8bef9SDimitry Andric   }
967e8d8bef9SDimitry Andric   ZeroCounterRatio = (float)ZeroCntNum / CntNum;
968bdd1243dSDimitry Andric   NumEdgeCounters = CntNum;
969e8d8bef9SDimitry Andric }
970e8d8bef9SDimitry Andric 
971bdd1243dSDimitry Andric /// Either set all the counters in the instr profile entry \p IFE to
972bdd1243dSDimitry Andric /// -1 / -2 /in order to drop the profile or scale up the
973bdd1243dSDimitry Andric /// counters in \p IFP to be above hot / cold threshold. We use
974bdd1243dSDimitry Andric /// the ratio of zero counters in the profile of a function to
975bdd1243dSDimitry Andric /// decide the profile is helpful or harmful for performance,
976bdd1243dSDimitry Andric /// and to choose whether to scale up or drop it.
updateInstrProfileEntry(InstrProfileEntry & IFE,bool SetToHot,uint64_t HotInstrThreshold,uint64_t ColdInstrThreshold,float ZeroCounterThreshold)977bdd1243dSDimitry Andric static void updateInstrProfileEntry(InstrProfileEntry &IFE, bool SetToHot,
978e8d8bef9SDimitry Andric                                     uint64_t HotInstrThreshold,
979bdd1243dSDimitry Andric                                     uint64_t ColdInstrThreshold,
980e8d8bef9SDimitry Andric                                     float ZeroCounterThreshold) {
981e8d8bef9SDimitry Andric   InstrProfRecord *ProfRecord = IFE.ProfRecord;
982e8d8bef9SDimitry Andric   if (!IFE.MaxCount || IFE.ZeroCounterRatio > ZeroCounterThreshold) {
983e8d8bef9SDimitry Andric     // If all or most of the counters of the function are zero, the
984bdd1243dSDimitry Andric     // profile is unaccountable and should be dropped. Reset all the
985bdd1243dSDimitry Andric     // counters to be -1 / -2 and PGO profile-use will drop the profile.
986e8d8bef9SDimitry Andric     // All counters being -1 also implies that the function is hot so
987e8d8bef9SDimitry Andric     // PGO profile-use will also set the entry count metadata to be
988e8d8bef9SDimitry Andric     // above hot threshold.
989bdd1243dSDimitry Andric     // All counters being -2 implies that the function is warm so
990bdd1243dSDimitry Andric     // PGO profile-use will also set the entry count metadata to be
991bdd1243dSDimitry Andric     // above cold threshold.
992bdd1243dSDimitry Andric     auto Kind =
993bdd1243dSDimitry Andric         (SetToHot ? InstrProfRecord::PseudoHot : InstrProfRecord::PseudoWarm);
994bdd1243dSDimitry Andric     ProfRecord->setPseudoCount(Kind);
995e8d8bef9SDimitry Andric     return;
996e8d8bef9SDimitry Andric   }
997e8d8bef9SDimitry Andric 
998bdd1243dSDimitry Andric   // Scale up the MaxCount to be multiple times above hot / cold threshold.
999e8d8bef9SDimitry Andric   const unsigned MultiplyFactor = 3;
1000bdd1243dSDimitry Andric   uint64_t Threshold = (SetToHot ? HotInstrThreshold : ColdInstrThreshold);
1001bdd1243dSDimitry Andric   uint64_t Numerator = Threshold * MultiplyFactor;
1002bdd1243dSDimitry Andric 
1003bdd1243dSDimitry Andric   // Make sure Threshold for warm counters is below the HotInstrThreshold.
1004bdd1243dSDimitry Andric   if (!SetToHot && Threshold >= HotInstrThreshold) {
1005bdd1243dSDimitry Andric     Threshold = (HotInstrThreshold + ColdInstrThreshold) / 2;
1006bdd1243dSDimitry Andric   }
1007bdd1243dSDimitry Andric 
1008e8d8bef9SDimitry Andric   uint64_t Denominator = IFE.MaxCount;
1009bdd1243dSDimitry Andric   if (Numerator <= Denominator)
1010bdd1243dSDimitry Andric     return;
1011e8d8bef9SDimitry Andric   ProfRecord->scale(Numerator, Denominator, [&](instrprof_error E) {
1012e8d8bef9SDimitry Andric     warn(toString(make_error<InstrProfError>(E)));
1013e8d8bef9SDimitry Andric   });
1014e8d8bef9SDimitry Andric }
1015e8d8bef9SDimitry Andric 
1016e8d8bef9SDimitry Andric const uint64_t ColdPercentileIdx = 15;
1017e8d8bef9SDimitry Andric const uint64_t HotPercentileIdx = 11;
1018e8d8bef9SDimitry Andric 
1019fe6060f1SDimitry Andric using sampleprof::FSDiscriminatorPass;
1020fe6060f1SDimitry Andric 
1021fe6060f1SDimitry Andric // Internal options to set FSDiscriminatorPass. Used in merge and show
1022fe6060f1SDimitry Andric // commands.
1023fe6060f1SDimitry Andric static cl::opt<FSDiscriminatorPass> FSDiscriminatorPassOption(
1024fe6060f1SDimitry Andric     "fs-discriminator-pass", cl::init(PassLast), cl::Hidden,
1025fe6060f1SDimitry Andric     cl::desc("Zero out the discriminator bits for the FS discrimiantor "
1026fe6060f1SDimitry Andric              "pass beyond this value. The enum values are defined in "
1027fe6060f1SDimitry Andric              "Support/Discriminator.h"),
1028fe6060f1SDimitry Andric     cl::values(clEnumVal(Base, "Use base discriminators only"),
1029fe6060f1SDimitry Andric                clEnumVal(Pass1, "Use base and pass 1 discriminators"),
1030fe6060f1SDimitry Andric                clEnumVal(Pass2, "Use base and pass 1-2 discriminators"),
1031fe6060f1SDimitry Andric                clEnumVal(Pass3, "Use base and pass 1-3 discriminators"),
1032fe6060f1SDimitry Andric                clEnumVal(PassLast, "Use all discriminator bits (default)")));
1033fe6060f1SDimitry Andric 
getDiscriminatorMask()1034fe6060f1SDimitry Andric static unsigned getDiscriminatorMask() {
1035fe6060f1SDimitry Andric   return getN1Bits(getFSPassBitEnd(FSDiscriminatorPassOption.getValue()));
1036fe6060f1SDimitry Andric }
1037fe6060f1SDimitry Andric 
1038e8d8bef9SDimitry Andric /// Adjust the instr profile in \p WC based on the sample profile in
1039e8d8bef9SDimitry Andric /// \p Reader.
1040e8d8bef9SDimitry Andric static void
adjustInstrProfile(std::unique_ptr<WriterContext> & WC,std::unique_ptr<sampleprof::SampleProfileReader> & Reader,unsigned SupplMinSizeThreshold,float ZeroCounterThreshold,unsigned InstrProfColdThreshold)1041e8d8bef9SDimitry Andric adjustInstrProfile(std::unique_ptr<WriterContext> &WC,
1042e8d8bef9SDimitry Andric                    std::unique_ptr<sampleprof::SampleProfileReader> &Reader,
1043e8d8bef9SDimitry Andric                    unsigned SupplMinSizeThreshold, float ZeroCounterThreshold,
1044e8d8bef9SDimitry Andric                    unsigned InstrProfColdThreshold) {
1045e8d8bef9SDimitry Andric   // Function to its entry in instr profile.
1046e8d8bef9SDimitry Andric   StringMap<InstrProfileEntry> InstrProfileMap;
1047bdd1243dSDimitry Andric   StringMap<StringRef> StaticFuncMap;
1048e8d8bef9SDimitry Andric   InstrProfSummaryBuilder IPBuilder(ProfileSummaryBuilder::DefaultCutoffs);
1049bdd1243dSDimitry Andric 
1050bdd1243dSDimitry Andric   auto checkSampleProfileHasFUnique = [&Reader]() {
1051bdd1243dSDimitry Andric     for (const auto &PD : Reader->getProfiles()) {
10525f757f3fSDimitry Andric       auto &FContext = PD.second.getContext();
1053bdd1243dSDimitry Andric       if (FContext.toString().find(FunctionSamples::UniqSuffix) !=
1054bdd1243dSDimitry Andric           std::string::npos) {
1055bdd1243dSDimitry Andric         return true;
1056bdd1243dSDimitry Andric       }
1057bdd1243dSDimitry Andric     }
1058bdd1243dSDimitry Andric     return false;
1059bdd1243dSDimitry Andric   };
1060bdd1243dSDimitry Andric 
1061bdd1243dSDimitry Andric   bool SampleProfileHasFUnique = checkSampleProfileHasFUnique();
1062bdd1243dSDimitry Andric 
1063bdd1243dSDimitry Andric   auto buildStaticFuncMap = [&StaticFuncMap,
1064bdd1243dSDimitry Andric                              SampleProfileHasFUnique](const StringRef Name) {
10651db9f3b2SDimitry Andric     std::string FilePrefixes[] = {".cpp", "cc", ".c", ".hpp", ".h"};
1066bdd1243dSDimitry Andric     size_t PrefixPos = StringRef::npos;
10671db9f3b2SDimitry Andric     for (auto &FilePrefix : FilePrefixes) {
10681db9f3b2SDimitry Andric       std::string NamePrefix = FilePrefix + kGlobalIdentifierDelimiter;
10691db9f3b2SDimitry Andric       PrefixPos = Name.find_insensitive(NamePrefix);
1070bdd1243dSDimitry Andric       if (PrefixPos == StringRef::npos)
1071bdd1243dSDimitry Andric         continue;
10721db9f3b2SDimitry Andric       PrefixPos += NamePrefix.size();
1073bdd1243dSDimitry Andric       break;
1074bdd1243dSDimitry Andric     }
1075bdd1243dSDimitry Andric 
1076bdd1243dSDimitry Andric     if (PrefixPos == StringRef::npos) {
1077bdd1243dSDimitry Andric       return;
1078bdd1243dSDimitry Andric     }
1079bdd1243dSDimitry Andric 
1080bdd1243dSDimitry Andric     StringRef NewName = Name.drop_front(PrefixPos);
1081bdd1243dSDimitry Andric     StringRef FName = Name.substr(0, PrefixPos - 1);
1082bdd1243dSDimitry Andric     if (NewName.size() == 0) {
1083bdd1243dSDimitry Andric       return;
1084bdd1243dSDimitry Andric     }
1085bdd1243dSDimitry Andric 
1086bdd1243dSDimitry Andric     // This name should have a static linkage.
1087bdd1243dSDimitry Andric     size_t PostfixPos = NewName.find(FunctionSamples::UniqSuffix);
1088bdd1243dSDimitry Andric     bool ProfileHasFUnique = (PostfixPos != StringRef::npos);
1089bdd1243dSDimitry Andric 
1090bdd1243dSDimitry Andric     // If sample profile and instrumented profile do not agree on symbol
1091bdd1243dSDimitry Andric     // uniqification.
1092bdd1243dSDimitry Andric     if (SampleProfileHasFUnique != ProfileHasFUnique) {
10935f757f3fSDimitry Andric       // If instrumented profile uses -funique-internal-linkage-symbols,
1094bdd1243dSDimitry Andric       // we need to trim the name.
1095bdd1243dSDimitry Andric       if (ProfileHasFUnique) {
1096bdd1243dSDimitry Andric         NewName = NewName.substr(0, PostfixPos);
1097bdd1243dSDimitry Andric       } else {
10985f757f3fSDimitry Andric         // If sample profile uses -funique-internal-linkage-symbols,
1099bdd1243dSDimitry Andric         // we build the map.
1100bdd1243dSDimitry Andric         std::string NStr =
1101bdd1243dSDimitry Andric             NewName.str() + getUniqueInternalLinkagePostfix(FName);
1102bdd1243dSDimitry Andric         NewName = StringRef(NStr);
1103bdd1243dSDimitry Andric         StaticFuncMap[NewName] = Name;
1104bdd1243dSDimitry Andric         return;
1105bdd1243dSDimitry Andric       }
1106bdd1243dSDimitry Andric     }
1107bdd1243dSDimitry Andric 
110806c3fb27SDimitry Andric     if (!StaticFuncMap.contains(NewName)) {
1109bdd1243dSDimitry Andric       StaticFuncMap[NewName] = Name;
1110bdd1243dSDimitry Andric     } else {
1111bdd1243dSDimitry Andric       StaticFuncMap[NewName] = DuplicateNameStr;
1112bdd1243dSDimitry Andric     }
1113bdd1243dSDimitry Andric   };
1114bdd1243dSDimitry Andric 
1115bdd1243dSDimitry Andric   // We need to flatten the SampleFDO profile as the InstrFDO
1116bdd1243dSDimitry Andric   // profile does not have inlined callsite profiles.
1117bdd1243dSDimitry Andric   // One caveat is the pre-inlined function -- their samples
1118bdd1243dSDimitry Andric   // should be collapsed into the caller function.
1119bdd1243dSDimitry Andric   // Here we do a DFS traversal to get the flatten profile
1120bdd1243dSDimitry Andric   // info: the sum of entrycount and the max of maxcount.
1121bdd1243dSDimitry Andric   // Here is the algorithm:
1122bdd1243dSDimitry Andric   //   recursive (FS, root_name) {
1123bdd1243dSDimitry Andric   //      name = FS->getName();
1124bdd1243dSDimitry Andric   //      get samples for FS;
1125bdd1243dSDimitry Andric   //      if (InstrProf.find(name) {
1126bdd1243dSDimitry Andric   //        root_name = name;
1127bdd1243dSDimitry Andric   //      } else {
1128bdd1243dSDimitry Andric   //        if (name is in static_func map) {
1129bdd1243dSDimitry Andric   //          root_name = static_name;
1130bdd1243dSDimitry Andric   //        }
1131bdd1243dSDimitry Andric   //      }
1132bdd1243dSDimitry Andric   //      update the Map entry for root_name;
1133bdd1243dSDimitry Andric   //      for (subfs: FS) {
1134bdd1243dSDimitry Andric   //        recursive(subfs, root_name);
1135bdd1243dSDimitry Andric   //      }
1136bdd1243dSDimitry Andric   //   }
1137bdd1243dSDimitry Andric   //
1138bdd1243dSDimitry Andric   // Here is an example.
1139bdd1243dSDimitry Andric   //
1140bdd1243dSDimitry Andric   // SampleProfile:
1141bdd1243dSDimitry Andric   // foo:12345:1000
1142bdd1243dSDimitry Andric   // 1: 1000
1143bdd1243dSDimitry Andric   // 2.1: 1000
1144bdd1243dSDimitry Andric   // 15: 5000
1145bdd1243dSDimitry Andric   // 4: bar:1000
1146bdd1243dSDimitry Andric   //  1: 1000
1147bdd1243dSDimitry Andric   //  2: goo:3000
1148bdd1243dSDimitry Andric   //   1: 3000
1149bdd1243dSDimitry Andric   // 8: bar:40000
1150bdd1243dSDimitry Andric   //  1: 10000
1151bdd1243dSDimitry Andric   //  2: goo:30000
1152bdd1243dSDimitry Andric   //   1: 30000
1153bdd1243dSDimitry Andric   //
1154bdd1243dSDimitry Andric   // InstrProfile has two entries:
1155bdd1243dSDimitry Andric   //  foo
11561db9f3b2SDimitry Andric   //  bar.cc;bar
1157bdd1243dSDimitry Andric   //
1158bdd1243dSDimitry Andric   // After BuildMaxSampleMap, we should have the following in FlattenSampleMap:
1159bdd1243dSDimitry Andric   // {"foo", {1000, 5000}}
11601db9f3b2SDimitry Andric   // {"bar.cc;bar", {11000, 30000}}
1161bdd1243dSDimitry Andric   //
1162bdd1243dSDimitry Andric   // foo's has an entry count of 1000, and max body count of 5000.
11631db9f3b2SDimitry Andric   // bar.cc;bar has an entry count of 11000 (sum two callsites of 1000 and
1164bdd1243dSDimitry Andric   // 10000), and max count of 30000 (from the callsite in line 8).
1165bdd1243dSDimitry Andric   //
11661db9f3b2SDimitry Andric   // Note that goo's count will remain in bar.cc;bar() as it does not have an
1167bdd1243dSDimitry Andric   // entry in InstrProfile.
11685f757f3fSDimitry Andric   llvm::StringMap<std::pair<uint64_t, uint64_t>> FlattenSampleMap;
1169bdd1243dSDimitry Andric   auto BuildMaxSampleMap = [&FlattenSampleMap, &StaticFuncMap,
1170bdd1243dSDimitry Andric                             &InstrProfileMap](const FunctionSamples &FS,
1171bdd1243dSDimitry Andric                                               const StringRef &RootName) {
1172bdd1243dSDimitry Andric     auto BuildMaxSampleMapImpl = [&](const FunctionSamples &FS,
1173bdd1243dSDimitry Andric                                      const StringRef &RootName,
1174bdd1243dSDimitry Andric                                      auto &BuildImpl) -> void {
11755f757f3fSDimitry Andric       std::string NameStr = FS.getFunction().str();
11765f757f3fSDimitry Andric       const StringRef Name = NameStr;
1177bdd1243dSDimitry Andric       const StringRef *NewRootName = &RootName;
1178bdd1243dSDimitry Andric       uint64_t EntrySample = FS.getHeadSamplesEstimate();
1179bdd1243dSDimitry Andric       uint64_t MaxBodySample = FS.getMaxCountInside(/* SkipCallSite*/ true);
1180bdd1243dSDimitry Andric 
1181bdd1243dSDimitry Andric       auto It = InstrProfileMap.find(Name);
1182bdd1243dSDimitry Andric       if (It != InstrProfileMap.end()) {
1183bdd1243dSDimitry Andric         NewRootName = &Name;
1184bdd1243dSDimitry Andric       } else {
1185bdd1243dSDimitry Andric         auto NewName = StaticFuncMap.find(Name);
1186bdd1243dSDimitry Andric         if (NewName != StaticFuncMap.end()) {
1187bdd1243dSDimitry Andric           It = InstrProfileMap.find(NewName->second.str());
1188bdd1243dSDimitry Andric           if (NewName->second != DuplicateNameStr) {
1189bdd1243dSDimitry Andric             NewRootName = &NewName->second;
1190bdd1243dSDimitry Andric           }
1191bdd1243dSDimitry Andric         } else {
1192bdd1243dSDimitry Andric           // Here the EntrySample is of an inlined function, so we should not
1193bdd1243dSDimitry Andric           // update the EntrySample in the map.
1194bdd1243dSDimitry Andric           EntrySample = 0;
1195bdd1243dSDimitry Andric         }
1196bdd1243dSDimitry Andric       }
1197bdd1243dSDimitry Andric       EntrySample += FlattenSampleMap[*NewRootName].first;
1198bdd1243dSDimitry Andric       MaxBodySample =
1199bdd1243dSDimitry Andric           std::max(FlattenSampleMap[*NewRootName].second, MaxBodySample);
1200bdd1243dSDimitry Andric       FlattenSampleMap[*NewRootName] =
1201bdd1243dSDimitry Andric           std::make_pair(EntrySample, MaxBodySample);
1202bdd1243dSDimitry Andric 
1203bdd1243dSDimitry Andric       for (const auto &C : FS.getCallsiteSamples())
1204bdd1243dSDimitry Andric         for (const auto &F : C.second)
1205bdd1243dSDimitry Andric           BuildImpl(F.second, *NewRootName, BuildImpl);
1206bdd1243dSDimitry Andric     };
1207bdd1243dSDimitry Andric     BuildMaxSampleMapImpl(FS, RootName, BuildMaxSampleMapImpl);
1208bdd1243dSDimitry Andric   };
1209bdd1243dSDimitry Andric 
1210e8d8bef9SDimitry Andric   for (auto &PD : WC->Writer.getProfileData()) {
1211e8d8bef9SDimitry Andric     // Populate IPBuilder.
1212e8d8bef9SDimitry Andric     for (const auto &PDV : PD.getValue()) {
1213e8d8bef9SDimitry Andric       InstrProfRecord Record = PDV.second;
1214e8d8bef9SDimitry Andric       IPBuilder.addRecord(Record);
1215e8d8bef9SDimitry Andric     }
1216e8d8bef9SDimitry Andric 
1217e8d8bef9SDimitry Andric     // If a function has multiple entries in instr profile, skip it.
1218e8d8bef9SDimitry Andric     if (PD.getValue().size() != 1)
1219e8d8bef9SDimitry Andric       continue;
1220e8d8bef9SDimitry Andric 
1221e8d8bef9SDimitry Andric     // Initialize InstrProfileMap.
1222e8d8bef9SDimitry Andric     InstrProfRecord *R = &PD.getValue().begin()->second;
1223bdd1243dSDimitry Andric     StringRef FullName = PD.getKey();
1224bdd1243dSDimitry Andric     InstrProfileMap[FullName] = InstrProfileEntry(R);
1225bdd1243dSDimitry Andric     buildStaticFuncMap(FullName);
1226bdd1243dSDimitry Andric   }
1227bdd1243dSDimitry Andric 
1228bdd1243dSDimitry Andric   for (auto &PD : Reader->getProfiles()) {
1229bdd1243dSDimitry Andric     sampleprof::FunctionSamples &FS = PD.second;
12305f757f3fSDimitry Andric     std::string Name = FS.getFunction().str();
12315f757f3fSDimitry Andric     BuildMaxSampleMap(FS, Name);
1232e8d8bef9SDimitry Andric   }
1233e8d8bef9SDimitry Andric 
1234e8d8bef9SDimitry Andric   ProfileSummary InstrPS = *IPBuilder.getSummary();
1235e8d8bef9SDimitry Andric   ProfileSummary SamplePS = Reader->getSummary();
1236e8d8bef9SDimitry Andric 
1237e8d8bef9SDimitry Andric   // Compute cold thresholds for instr profile and sample profile.
1238bdd1243dSDimitry Andric   uint64_t HotSampleThreshold =
1239bdd1243dSDimitry Andric       ProfileSummaryBuilder::getEntryForPercentile(
1240bdd1243dSDimitry Andric           SamplePS.getDetailedSummary(),
1241bdd1243dSDimitry Andric           ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx])
1242bdd1243dSDimitry Andric           .MinCount;
1243e8d8bef9SDimitry Andric   uint64_t ColdSampleThreshold =
1244e8d8bef9SDimitry Andric       ProfileSummaryBuilder::getEntryForPercentile(
1245e8d8bef9SDimitry Andric           SamplePS.getDetailedSummary(),
1246e8d8bef9SDimitry Andric           ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx])
1247e8d8bef9SDimitry Andric           .MinCount;
1248e8d8bef9SDimitry Andric   uint64_t HotInstrThreshold =
1249e8d8bef9SDimitry Andric       ProfileSummaryBuilder::getEntryForPercentile(
1250e8d8bef9SDimitry Andric           InstrPS.getDetailedSummary(),
1251e8d8bef9SDimitry Andric           ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx])
1252e8d8bef9SDimitry Andric           .MinCount;
1253e8d8bef9SDimitry Andric   uint64_t ColdInstrThreshold =
1254e8d8bef9SDimitry Andric       InstrProfColdThreshold
1255e8d8bef9SDimitry Andric           ? InstrProfColdThreshold
1256e8d8bef9SDimitry Andric           : ProfileSummaryBuilder::getEntryForPercentile(
1257e8d8bef9SDimitry Andric                 InstrPS.getDetailedSummary(),
1258e8d8bef9SDimitry Andric                 ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx])
1259e8d8bef9SDimitry Andric                 .MinCount;
1260e8d8bef9SDimitry Andric 
1261e8d8bef9SDimitry Andric   // Find hot/warm functions in sample profile which is cold in instr profile
1262e8d8bef9SDimitry Andric   // and adjust the profiles of those functions in the instr profile.
1263bdd1243dSDimitry Andric   for (const auto &E : FlattenSampleMap) {
1264bdd1243dSDimitry Andric     uint64_t SampleMaxCount = std::max(E.second.first, E.second.second);
1265bdd1243dSDimitry Andric     if (SampleMaxCount < ColdSampleThreshold)
1266bdd1243dSDimitry Andric       continue;
12675f757f3fSDimitry Andric     StringRef Name = E.first();
1268bdd1243dSDimitry Andric     auto It = InstrProfileMap.find(Name);
1269bdd1243dSDimitry Andric     if (It == InstrProfileMap.end()) {
1270bdd1243dSDimitry Andric       auto NewName = StaticFuncMap.find(Name);
1271bdd1243dSDimitry Andric       if (NewName != StaticFuncMap.end()) {
1272bdd1243dSDimitry Andric         It = InstrProfileMap.find(NewName->second.str());
1273bdd1243dSDimitry Andric         if (NewName->second == DuplicateNameStr) {
1274bdd1243dSDimitry Andric           WithColor::warning()
1275bdd1243dSDimitry Andric               << "Static function " << Name
1276bdd1243dSDimitry Andric               << " has multiple promoted names, cannot adjust profile.\n";
1277e8d8bef9SDimitry Andric         }
1278e8d8bef9SDimitry Andric       }
1279e8d8bef9SDimitry Andric     }
1280bdd1243dSDimitry Andric     if (It == InstrProfileMap.end() ||
1281bdd1243dSDimitry Andric         It->second.MaxCount > ColdInstrThreshold ||
1282bdd1243dSDimitry Andric         It->second.NumEdgeCounters < SupplMinSizeThreshold)
1283bdd1243dSDimitry Andric       continue;
1284bdd1243dSDimitry Andric     bool SetToHot = SampleMaxCount >= HotSampleThreshold;
1285bdd1243dSDimitry Andric     updateInstrProfileEntry(It->second, SetToHot, HotInstrThreshold,
1286bdd1243dSDimitry Andric                             ColdInstrThreshold, ZeroCounterThreshold);
1287bdd1243dSDimitry Andric   }
1288bdd1243dSDimitry Andric }
1289e8d8bef9SDimitry Andric 
1290e8d8bef9SDimitry Andric /// The main function to supplement instr profile with sample profile.
1291e8d8bef9SDimitry Andric /// \Inputs contains the instr profile. \p SampleFilename specifies the
1292e8d8bef9SDimitry Andric /// sample profile. \p OutputFilename specifies the output profile name.
1293e8d8bef9SDimitry Andric /// \p OutputFormat specifies the output profile format. \p OutputSparse
1294e8d8bef9SDimitry Andric /// specifies whether to generate sparse profile. \p SupplMinSizeThreshold
1295e8d8bef9SDimitry Andric /// specifies the minimal size for the functions whose profile will be
1296e8d8bef9SDimitry Andric /// adjusted. \p ZeroCounterThreshold is the threshold to check whether
1297e8d8bef9SDimitry Andric /// a function contains too many zero counters and whether its profile
1298e8d8bef9SDimitry Andric /// should be dropped. \p InstrProfColdThreshold is the user specified
1299e8d8bef9SDimitry Andric /// cold threshold which will override the cold threshold got from the
1300e8d8bef9SDimitry Andric /// instr profile summary.
supplementInstrProfile(const WeightedFileVector & Inputs,StringRef SampleFilename,bool OutputSparse,unsigned SupplMinSizeThreshold,float ZeroCounterThreshold,unsigned InstrProfColdThreshold)13015f757f3fSDimitry Andric static void supplementInstrProfile(const WeightedFileVector &Inputs,
13025f757f3fSDimitry Andric                                    StringRef SampleFilename, bool OutputSparse,
13035f757f3fSDimitry Andric                                    unsigned SupplMinSizeThreshold,
13045f757f3fSDimitry Andric                                    float ZeroCounterThreshold,
1305e8d8bef9SDimitry Andric                                    unsigned InstrProfColdThreshold) {
1306e8d8bef9SDimitry Andric   if (OutputFilename.compare("-") == 0)
1307fe6060f1SDimitry Andric     exitWithError("cannot write indexed profdata format to stdout");
1308e8d8bef9SDimitry Andric   if (Inputs.size() != 1)
1309fe6060f1SDimitry Andric     exitWithError("expect one input to be an instr profile");
1310e8d8bef9SDimitry Andric   if (Inputs[0].Weight != 1)
1311fe6060f1SDimitry Andric     exitWithError("expect instr profile doesn't have weight");
1312e8d8bef9SDimitry Andric 
1313e8d8bef9SDimitry Andric   StringRef InstrFilename = Inputs[0].Filename;
1314e8d8bef9SDimitry Andric 
1315e8d8bef9SDimitry Andric   // Read sample profile.
1316e8d8bef9SDimitry Andric   LLVMContext Context;
131706c3fb27SDimitry Andric   auto FS = vfs::getRealFileSystem();
1318fe6060f1SDimitry Andric   auto ReaderOrErr = sampleprof::SampleProfileReader::create(
131906c3fb27SDimitry Andric       SampleFilename.str(), Context, *FS, FSDiscriminatorPassOption);
1320e8d8bef9SDimitry Andric   if (std::error_code EC = ReaderOrErr.getError())
1321e8d8bef9SDimitry Andric     exitWithErrorCode(EC, SampleFilename);
1322e8d8bef9SDimitry Andric   auto Reader = std::move(ReaderOrErr.get());
1323e8d8bef9SDimitry Andric   if (std::error_code EC = Reader->read())
1324e8d8bef9SDimitry Andric     exitWithErrorCode(EC, SampleFilename);
1325e8d8bef9SDimitry Andric 
1326e8d8bef9SDimitry Andric   // Read instr profile.
1327e8d8bef9SDimitry Andric   std::mutex ErrorLock;
1328e8d8bef9SDimitry Andric   SmallSet<instrprof_error, 4> WriterErrorCodes;
1329e8d8bef9SDimitry Andric   auto WC = std::make_unique<WriterContext>(OutputSparse, ErrorLock,
1330e8d8bef9SDimitry Andric                                             WriterErrorCodes);
133181ad6265SDimitry Andric   loadInput(Inputs[0], nullptr, nullptr, /*ProfiledBinary=*/"", WC.get());
1332e8d8bef9SDimitry Andric   if (WC->Errors.size() > 0)
1333e8d8bef9SDimitry Andric     exitWithError(std::move(WC->Errors[0].first), InstrFilename);
1334e8d8bef9SDimitry Andric 
1335e8d8bef9SDimitry Andric   adjustInstrProfile(WC, Reader, SupplMinSizeThreshold, ZeroCounterThreshold,
1336e8d8bef9SDimitry Andric                      InstrProfColdThreshold);
1337e8d8bef9SDimitry Andric   writeInstrProfile(OutputFilename, OutputFormat, WC->Writer);
1338e8d8bef9SDimitry Andric }
1339e8d8bef9SDimitry Andric 
13400b57cec5SDimitry Andric /// Make a copy of the given function samples with all symbol names remapped
13410b57cec5SDimitry Andric /// by the provided symbol remapper.
13420b57cec5SDimitry Andric static sampleprof::FunctionSamples
remapSamples(const sampleprof::FunctionSamples & Samples,SymbolRemapper & Remapper,sampleprof_error & Error)13430b57cec5SDimitry Andric remapSamples(const sampleprof::FunctionSamples &Samples,
13440b57cec5SDimitry Andric              SymbolRemapper &Remapper, sampleprof_error &Error) {
13450b57cec5SDimitry Andric   sampleprof::FunctionSamples Result;
13465f757f3fSDimitry Andric   Result.setFunction(Remapper(Samples.getFunction()));
13470b57cec5SDimitry Andric   Result.addTotalSamples(Samples.getTotalSamples());
13480b57cec5SDimitry Andric   Result.addHeadSamples(Samples.getHeadSamples());
13490b57cec5SDimitry Andric   for (const auto &BodySample : Samples.getBodySamples()) {
1350fe6060f1SDimitry Andric     uint32_t MaskedDiscriminator =
1351fe6060f1SDimitry Andric         BodySample.first.Discriminator & getDiscriminatorMask();
1352fe6060f1SDimitry Andric     Result.addBodySamples(BodySample.first.LineOffset, MaskedDiscriminator,
13530b57cec5SDimitry Andric                           BodySample.second.getSamples());
13540b57cec5SDimitry Andric     for (const auto &Target : BodySample.second.getCallTargets()) {
13550b57cec5SDimitry Andric       Result.addCalledTargetSamples(BodySample.first.LineOffset,
1356fe6060f1SDimitry Andric                                     MaskedDiscriminator,
13575f757f3fSDimitry Andric                                     Remapper(Target.first), Target.second);
13580b57cec5SDimitry Andric     }
13590b57cec5SDimitry Andric   }
13600b57cec5SDimitry Andric   for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) {
13610b57cec5SDimitry Andric     sampleprof::FunctionSamplesMap &Target =
13620b57cec5SDimitry Andric         Result.functionSamplesAt(CallsiteSamples.first);
13630b57cec5SDimitry Andric     for (const auto &Callsite : CallsiteSamples.second) {
13640b57cec5SDimitry Andric       sampleprof::FunctionSamples Remapped =
13650b57cec5SDimitry Andric           remapSamples(Callsite.second, Remapper, Error);
13665f757f3fSDimitry Andric       MergeResult(Error, Target[Remapped.getFunction()].merge(Remapped));
13670b57cec5SDimitry Andric     }
13680b57cec5SDimitry Andric   }
13690b57cec5SDimitry Andric   return Result;
13700b57cec5SDimitry Andric }
13710b57cec5SDimitry Andric 
13720b57cec5SDimitry Andric static sampleprof::SampleProfileFormat FormatMap[] = {
13738bcb0991SDimitry Andric     sampleprof::SPF_None,
13748bcb0991SDimitry Andric     sampleprof::SPF_Text,
137506c3fb27SDimitry Andric     sampleprof::SPF_None,
13768bcb0991SDimitry Andric     sampleprof::SPF_Ext_Binary,
13778bcb0991SDimitry Andric     sampleprof::SPF_GCC,
13788bcb0991SDimitry Andric     sampleprof::SPF_Binary};
13798bcb0991SDimitry Andric 
13808bcb0991SDimitry Andric static std::unique_ptr<MemoryBuffer>
getInputFileBuf(const StringRef & InputFile)13818bcb0991SDimitry Andric getInputFileBuf(const StringRef &InputFile) {
13828bcb0991SDimitry Andric   if (InputFile == "")
13838bcb0991SDimitry Andric     return {};
13848bcb0991SDimitry Andric 
13858bcb0991SDimitry Andric   auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);
13868bcb0991SDimitry Andric   if (!BufOrError)
13878bcb0991SDimitry Andric     exitWithErrorCode(BufOrError.getError(), InputFile);
13888bcb0991SDimitry Andric 
13898bcb0991SDimitry Andric   return std::move(*BufOrError);
13908bcb0991SDimitry Andric }
13918bcb0991SDimitry Andric 
populateProfileSymbolList(MemoryBuffer * Buffer,sampleprof::ProfileSymbolList & PSL)13928bcb0991SDimitry Andric static void populateProfileSymbolList(MemoryBuffer *Buffer,
13938bcb0991SDimitry Andric                                       sampleprof::ProfileSymbolList &PSL) {
13948bcb0991SDimitry Andric   if (!Buffer)
13958bcb0991SDimitry Andric     return;
13968bcb0991SDimitry Andric 
13978bcb0991SDimitry Andric   SmallVector<StringRef, 32> SymbolVec;
13988bcb0991SDimitry Andric   StringRef Data = Buffer->getBuffer();
13998bcb0991SDimitry Andric   Data.split(SymbolVec, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
14008bcb0991SDimitry Andric 
140104eeddc0SDimitry Andric   for (StringRef SymbolStr : SymbolVec)
140204eeddc0SDimitry Andric     PSL.add(SymbolStr.trim());
14038bcb0991SDimitry Andric }
14048bcb0991SDimitry Andric 
handleExtBinaryWriter(sampleprof::SampleProfileWriter & Writer,ProfileFormat OutputFormat,MemoryBuffer * Buffer,sampleprof::ProfileSymbolList & WriterList,bool CompressAllSections,bool UseMD5,bool GenPartialProfile)14058bcb0991SDimitry Andric static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer,
14068bcb0991SDimitry Andric                                   ProfileFormat OutputFormat,
14078bcb0991SDimitry Andric                                   MemoryBuffer *Buffer,
14088bcb0991SDimitry Andric                                   sampleprof::ProfileSymbolList &WriterList,
14095ffd83dbSDimitry Andric                                   bool CompressAllSections, bool UseMD5,
14105ffd83dbSDimitry Andric                                   bool GenPartialProfile) {
14118bcb0991SDimitry Andric   populateProfileSymbolList(Buffer, WriterList);
14128bcb0991SDimitry Andric   if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary)
14138bcb0991SDimitry Andric     warn("Profile Symbol list is not empty but the output format is not "
14148bcb0991SDimitry Andric          "ExtBinary format. The list will be lost in the output. ");
14158bcb0991SDimitry Andric 
14168bcb0991SDimitry Andric   Writer.setProfileSymbolList(&WriterList);
14178bcb0991SDimitry Andric 
14188bcb0991SDimitry Andric   if (CompressAllSections) {
14195ffd83dbSDimitry Andric     if (OutputFormat != PF_Ext_Binary)
14208bcb0991SDimitry Andric       warn("-compress-all-section is ignored. Specify -extbinary to enable it");
14215ffd83dbSDimitry Andric     else
14225ffd83dbSDimitry Andric       Writer.setToCompressAllSections();
14238bcb0991SDimitry Andric   }
14245ffd83dbSDimitry Andric   if (UseMD5) {
14255ffd83dbSDimitry Andric     if (OutputFormat != PF_Ext_Binary)
14265ffd83dbSDimitry Andric       warn("-use-md5 is ignored. Specify -extbinary to enable it");
14275ffd83dbSDimitry Andric     else
14285ffd83dbSDimitry Andric       Writer.setUseMD5();
14295ffd83dbSDimitry Andric   }
14305ffd83dbSDimitry Andric   if (GenPartialProfile) {
14315ffd83dbSDimitry Andric     if (OutputFormat != PF_Ext_Binary)
14325ffd83dbSDimitry Andric       warn("-gen-partial-profile is ignored. Specify -extbinary to enable it");
14335ffd83dbSDimitry Andric     else
14345ffd83dbSDimitry Andric       Writer.setPartialProfile();
14358bcb0991SDimitry Andric   }
14368bcb0991SDimitry Andric }
14370b57cec5SDimitry Andric 
mergeSampleProfile(const WeightedFileVector & Inputs,SymbolRemapper * Remapper,StringRef ProfileSymbolListFile,size_t OutputSizeLimit)14385f757f3fSDimitry Andric static void mergeSampleProfile(const WeightedFileVector &Inputs,
14395f757f3fSDimitry Andric                                SymbolRemapper *Remapper,
14405f757f3fSDimitry Andric                                StringRef ProfileSymbolListFile,
14415f757f3fSDimitry Andric                                size_t OutputSizeLimit) {
14420b57cec5SDimitry Andric   using namespace sampleprof;
1443349cc55cSDimitry Andric   SampleProfileMap ProfileMap;
14440b57cec5SDimitry Andric   SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers;
14450b57cec5SDimitry Andric   LLVMContext Context;
14468bcb0991SDimitry Andric   sampleprof::ProfileSymbolList WriterList;
1447bdd1243dSDimitry Andric   std::optional<bool> ProfileIsProbeBased;
1448bdd1243dSDimitry Andric   std::optional<bool> ProfileIsCS;
14490b57cec5SDimitry Andric   for (const auto &Input : Inputs) {
145006c3fb27SDimitry Andric     auto FS = vfs::getRealFileSystem();
145106c3fb27SDimitry Andric     auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context, *FS,
1452fe6060f1SDimitry Andric                                                    FSDiscriminatorPassOption);
14538bcb0991SDimitry Andric     if (std::error_code EC = ReaderOrErr.getError()) {
14548bcb0991SDimitry Andric       warnOrExitGivenError(FailMode, EC, Input.Filename);
14558bcb0991SDimitry Andric       continue;
14568bcb0991SDimitry Andric     }
14570b57cec5SDimitry Andric 
14580b57cec5SDimitry Andric     // We need to keep the readers around until after all the files are
14590b57cec5SDimitry Andric     // read so that we do not lose the function names stored in each
14600b57cec5SDimitry Andric     // reader's memory. The function names are needed to write out the
14610b57cec5SDimitry Andric     // merged profile map.
14620b57cec5SDimitry Andric     Readers.push_back(std::move(ReaderOrErr.get()));
14630b57cec5SDimitry Andric     const auto Reader = Readers.back().get();
14648bcb0991SDimitry Andric     if (std::error_code EC = Reader->read()) {
14658bcb0991SDimitry Andric       warnOrExitGivenError(FailMode, EC, Input.Filename);
14668bcb0991SDimitry Andric       Readers.pop_back();
14678bcb0991SDimitry Andric       continue;
14688bcb0991SDimitry Andric     }
14690b57cec5SDimitry Andric 
1470349cc55cSDimitry Andric     SampleProfileMap &Profiles = Reader->getProfiles();
147181ad6265SDimitry Andric     if (ProfileIsProbeBased &&
1472e8d8bef9SDimitry Andric         ProfileIsProbeBased != FunctionSamples::ProfileIsProbeBased)
1473e8d8bef9SDimitry Andric       exitWithError(
1474e8d8bef9SDimitry Andric           "cannot merge probe-based profile with non-probe-based profile");
1475e8d8bef9SDimitry Andric     ProfileIsProbeBased = FunctionSamples::ProfileIsProbeBased;
147681ad6265SDimitry Andric     if (ProfileIsCS && ProfileIsCS != FunctionSamples::ProfileIsCS)
1477fe6060f1SDimitry Andric       exitWithError("cannot merge CS profile with non-CS profile");
147881ad6265SDimitry Andric     ProfileIsCS = FunctionSamples::ProfileIsCS;
1479349cc55cSDimitry Andric     for (SampleProfileMap::iterator I = Profiles.begin(), E = Profiles.end();
14800b57cec5SDimitry Andric          I != E; ++I) {
14810b57cec5SDimitry Andric       sampleprof_error Result = sampleprof_error::success;
14820b57cec5SDimitry Andric       FunctionSamples Remapped =
14830b57cec5SDimitry Andric           Remapper ? remapSamples(I->second, *Remapper, Result)
14840b57cec5SDimitry Andric                    : FunctionSamples();
14850b57cec5SDimitry Andric       FunctionSamples &Samples = Remapper ? Remapped : I->second;
1486349cc55cSDimitry Andric       SampleContext FContext = Samples.getContext();
1487349cc55cSDimitry Andric       MergeResult(Result, ProfileMap[FContext].merge(Samples, Input.Weight));
14880b57cec5SDimitry Andric       if (Result != sampleprof_error::success) {
14890b57cec5SDimitry Andric         std::error_code EC = make_error_code(Result);
1490349cc55cSDimitry Andric         handleMergeWriterError(errorCodeToError(EC), Input.Filename,
1491349cc55cSDimitry Andric                                FContext.toString());
14920b57cec5SDimitry Andric       }
14930b57cec5SDimitry Andric     }
14948bcb0991SDimitry Andric 
1495bdd1243dSDimitry Andric     if (!DropProfileSymbolList) {
14968bcb0991SDimitry Andric       std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =
14978bcb0991SDimitry Andric           Reader->getProfileSymbolList();
14988bcb0991SDimitry Andric       if (ReaderList)
14998bcb0991SDimitry Andric         WriterList.merge(*ReaderList);
15000b57cec5SDimitry Andric     }
1501bdd1243dSDimitry Andric   }
1502fe6060f1SDimitry Andric 
150381ad6265SDimitry Andric   if (ProfileIsCS && (SampleMergeColdContext || SampleTrimColdContext)) {
1504fe6060f1SDimitry Andric     // Use threshold calculated from profile summary unless specified.
1505fe6060f1SDimitry Andric     SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
1506fe6060f1SDimitry Andric     auto Summary = Builder.computeSummaryForProfiles(ProfileMap);
1507fe6060f1SDimitry Andric     uint64_t SampleProfColdThreshold =
1508fe6060f1SDimitry Andric         ProfileSummaryBuilder::getColdCountThreshold(
1509fe6060f1SDimitry Andric             (Summary->getDetailedSummary()));
1510fe6060f1SDimitry Andric 
1511fe6060f1SDimitry Andric     // Trim and merge cold context profile using cold threshold above;
1512fe6060f1SDimitry Andric     SampleContextTrimmer(ProfileMap)
1513fe6060f1SDimitry Andric         .trimAndMergeColdContextProfiles(
1514fe6060f1SDimitry Andric             SampleProfColdThreshold, SampleTrimColdContext,
1515349cc55cSDimitry Andric             SampleMergeColdContext, SampleColdContextFrameDepth, false);
1516fe6060f1SDimitry Andric   }
1517fe6060f1SDimitry Andric 
151806c3fb27SDimitry Andric   if (ProfileLayout == llvm::sampleprof::SPL_Flat) {
151906c3fb27SDimitry Andric     ProfileConverter::flattenProfile(ProfileMap, FunctionSamples::ProfileIsCS);
152006c3fb27SDimitry Andric     ProfileIsCS = FunctionSamples::ProfileIsCS = false;
152106c3fb27SDimitry Andric   } else if (ProfileIsCS && ProfileLayout == llvm::sampleprof::SPL_Nest) {
152206c3fb27SDimitry Andric     ProfileConverter CSConverter(ProfileMap);
152306c3fb27SDimitry Andric     CSConverter.convertCSProfiles();
152481ad6265SDimitry Andric     ProfileIsCS = FunctionSamples::ProfileIsCS = false;
15250eae32dcSDimitry Andric   }
15260eae32dcSDimitry Andric 
15277a6dacacSDimitry Andric   filterFunctions(ProfileMap);
15287a6dacacSDimitry Andric 
15290b57cec5SDimitry Andric   auto WriterOrErr =
15300b57cec5SDimitry Andric       SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]);
15310b57cec5SDimitry Andric   if (std::error_code EC = WriterOrErr.getError())
15320b57cec5SDimitry Andric     exitWithErrorCode(EC, OutputFilename);
15330b57cec5SDimitry Andric 
15340b57cec5SDimitry Andric   auto Writer = std::move(WriterOrErr.get());
15358bcb0991SDimitry Andric   // WriterList will have StringRef refering to string in Buffer.
15368bcb0991SDimitry Andric   // Make sure Buffer lives as long as WriterList.
15378bcb0991SDimitry Andric   auto Buffer = getInputFileBuf(ProfileSymbolListFile);
15388bcb0991SDimitry Andric   handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList,
15395ffd83dbSDimitry Andric                         CompressAllSections, UseMD5, GenPartialProfile);
154006c3fb27SDimitry Andric 
154106c3fb27SDimitry Andric   // If OutputSizeLimit is 0 (default), it is the same as write().
154206c3fb27SDimitry Andric   if (std::error_code EC =
154306c3fb27SDimitry Andric           Writer->writeWithSizeLimit(ProfileMap, OutputSizeLimit))
1544fe6060f1SDimitry Andric     exitWithErrorCode(std::move(EC));
15450b57cec5SDimitry Andric }
15460b57cec5SDimitry Andric 
parseWeightedFile(const StringRef & WeightedFilename)15470b57cec5SDimitry Andric static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) {
15480b57cec5SDimitry Andric   StringRef WeightStr, FileName;
15490b57cec5SDimitry Andric   std::tie(WeightStr, FileName) = WeightedFilename.split(',');
15500b57cec5SDimitry Andric 
15510b57cec5SDimitry Andric   uint64_t Weight;
15520b57cec5SDimitry Andric   if (WeightStr.getAsInteger(10, Weight) || Weight < 1)
1553fe6060f1SDimitry Andric     exitWithError("input weight must be a positive integer");
15540b57cec5SDimitry Andric 
15555ffd83dbSDimitry Andric   return {std::string(FileName), Weight};
15560b57cec5SDimitry Andric }
15570b57cec5SDimitry Andric 
addWeightedInput(WeightedFileVector & WNI,const WeightedFile & WF)15580b57cec5SDimitry Andric static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) {
15590b57cec5SDimitry Andric   StringRef Filename = WF.Filename;
15600b57cec5SDimitry Andric   uint64_t Weight = WF.Weight;
15610b57cec5SDimitry Andric 
15620b57cec5SDimitry Andric   // If it's STDIN just pass it on.
15630b57cec5SDimitry Andric   if (Filename == "-") {
15645ffd83dbSDimitry Andric     WNI.push_back({std::string(Filename), Weight});
15650b57cec5SDimitry Andric     return;
15660b57cec5SDimitry Andric   }
15670b57cec5SDimitry Andric 
15680b57cec5SDimitry Andric   llvm::sys::fs::file_status Status;
15690b57cec5SDimitry Andric   llvm::sys::fs::status(Filename, Status);
15700b57cec5SDimitry Andric   if (!llvm::sys::fs::exists(Status))
15710b57cec5SDimitry Andric     exitWithErrorCode(make_error_code(errc::no_such_file_or_directory),
15720b57cec5SDimitry Andric                       Filename);
15730b57cec5SDimitry Andric   // If it's a source file, collect it.
15740b57cec5SDimitry Andric   if (llvm::sys::fs::is_regular_file(Status)) {
15755ffd83dbSDimitry Andric     WNI.push_back({std::string(Filename), Weight});
15760b57cec5SDimitry Andric     return;
15770b57cec5SDimitry Andric   }
15780b57cec5SDimitry Andric 
15790b57cec5SDimitry Andric   if (llvm::sys::fs::is_directory(Status)) {
15800b57cec5SDimitry Andric     std::error_code EC;
15810b57cec5SDimitry Andric     for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E;
15820b57cec5SDimitry Andric          F != E && !EC; F.increment(EC)) {
15830b57cec5SDimitry Andric       if (llvm::sys::fs::is_regular_file(F->path())) {
15840b57cec5SDimitry Andric         addWeightedInput(WNI, {F->path(), Weight});
15850b57cec5SDimitry Andric       }
15860b57cec5SDimitry Andric     }
15870b57cec5SDimitry Andric     if (EC)
15880b57cec5SDimitry Andric       exitWithErrorCode(EC, Filename);
15890b57cec5SDimitry Andric   }
15900b57cec5SDimitry Andric }
15910b57cec5SDimitry Andric 
parseInputFilenamesFile(MemoryBuffer * Buffer,WeightedFileVector & WFV)15920b57cec5SDimitry Andric static void parseInputFilenamesFile(MemoryBuffer *Buffer,
15930b57cec5SDimitry Andric                                     WeightedFileVector &WFV) {
15940b57cec5SDimitry Andric   if (!Buffer)
15950b57cec5SDimitry Andric     return;
15960b57cec5SDimitry Andric 
15970b57cec5SDimitry Andric   SmallVector<StringRef, 8> Entries;
15980b57cec5SDimitry Andric   StringRef Data = Buffer->getBuffer();
15990b57cec5SDimitry Andric   Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
16000b57cec5SDimitry Andric   for (const StringRef &FileWeightEntry : Entries) {
16010b57cec5SDimitry Andric     StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r");
16020b57cec5SDimitry Andric     // Skip comments.
16035f757f3fSDimitry Andric     if (SanitizedEntry.starts_with("#"))
16040b57cec5SDimitry Andric       continue;
16050b57cec5SDimitry Andric     // If there's no comma, it's an unweighted profile.
1606349cc55cSDimitry Andric     else if (!SanitizedEntry.contains(','))
16075ffd83dbSDimitry Andric       addWeightedInput(WFV, {std::string(SanitizedEntry), 1});
16080b57cec5SDimitry Andric     else
16090b57cec5SDimitry Andric       addWeightedInput(WFV, parseWeightedFile(SanitizedEntry));
16100b57cec5SDimitry Andric   }
16110b57cec5SDimitry Andric }
16120b57cec5SDimitry Andric 
merge_main(int argc,const char * argv[])16130b57cec5SDimitry Andric static int merge_main(int argc, const char *argv[]) {
16140b57cec5SDimitry Andric   WeightedFileVector WeightedInputs;
16150b57cec5SDimitry Andric   for (StringRef Filename : InputFilenames)
16165ffd83dbSDimitry Andric     addWeightedInput(WeightedInputs, {std::string(Filename), 1});
16170b57cec5SDimitry Andric   for (StringRef WeightedFilename : WeightedInputFilenames)
16180b57cec5SDimitry Andric     addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename));
16190b57cec5SDimitry Andric 
16200b57cec5SDimitry Andric   // Make sure that the file buffer stays alive for the duration of the
16210b57cec5SDimitry Andric   // weighted input vector's lifetime.
16228bcb0991SDimitry Andric   auto Buffer = getInputFileBuf(InputFilenamesFile);
16230b57cec5SDimitry Andric   parseInputFilenamesFile(Buffer.get(), WeightedInputs);
16240b57cec5SDimitry Andric 
16250b57cec5SDimitry Andric   if (WeightedInputs.empty())
1626fe6060f1SDimitry Andric     exitWithError("no input files specified. See " +
16275f757f3fSDimitry Andric                   sys::path::filename(argv[0]) + " " + argv[1] + " -help");
16280b57cec5SDimitry Andric 
16290b57cec5SDimitry Andric   if (DumpInputFileList) {
16300b57cec5SDimitry Andric     for (auto &WF : WeightedInputs)
16310b57cec5SDimitry Andric       outs() << WF.Weight << "," << WF.Filename << "\n";
16320b57cec5SDimitry Andric     return 0;
16330b57cec5SDimitry Andric   }
16340b57cec5SDimitry Andric 
16350b57cec5SDimitry Andric   std::unique_ptr<SymbolRemapper> Remapper;
16360b57cec5SDimitry Andric   if (!RemappingFile.empty())
16370b57cec5SDimitry Andric     Remapper = SymbolRemapper::create(RemappingFile);
16380b57cec5SDimitry Andric 
1639e8d8bef9SDimitry Andric   if (!SupplInstrWithSample.empty()) {
1640e8d8bef9SDimitry Andric     if (ProfileKind != instr)
1641e8d8bef9SDimitry Andric       exitWithError(
1642e8d8bef9SDimitry Andric           "-supplement-instr-with-sample can only work with -instr. ");
1643e8d8bef9SDimitry Andric 
16445f757f3fSDimitry Andric     supplementInstrProfile(WeightedInputs, SupplInstrWithSample, OutputSparse,
16455f757f3fSDimitry Andric                            SupplMinSizeThreshold, ZeroCounterThreshold,
16465f757f3fSDimitry Andric                            InstrProfColdThreshold);
1647e8d8bef9SDimitry Andric     return 0;
1648e8d8bef9SDimitry Andric   }
1649e8d8bef9SDimitry Andric 
16500b57cec5SDimitry Andric   if (ProfileKind == instr)
16515f757f3fSDimitry Andric     mergeInstrProfile(WeightedInputs, Remapper.get(), MaxDbgCorrelationWarnings,
16525f757f3fSDimitry Andric                       ProfiledBinary);
16530b57cec5SDimitry Andric   else
16545f757f3fSDimitry Andric     mergeSampleProfile(WeightedInputs, Remapper.get(), ProfileSymbolListFile,
16555f757f3fSDimitry Andric                        OutputSizeLimit);
16560b57cec5SDimitry Andric   return 0;
16570b57cec5SDimitry Andric }
16580b57cec5SDimitry Andric 
16590b57cec5SDimitry Andric /// 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)16600b57cec5SDimitry Andric static void overlapInstrProfile(const std::string &BaseFilename,
16610b57cec5SDimitry Andric                                 const std::string &TestFilename,
16620b57cec5SDimitry Andric                                 const OverlapFuncFilters &FuncFilter,
16630b57cec5SDimitry Andric                                 raw_fd_ostream &OS, bool IsCS) {
16640b57cec5SDimitry Andric   std::mutex ErrorLock;
16650b57cec5SDimitry Andric   SmallSet<instrprof_error, 4> WriterErrorCodes;
16660b57cec5SDimitry Andric   WriterContext Context(false, ErrorLock, WriterErrorCodes);
16670b57cec5SDimitry Andric   WeightedFile WeightedInput{BaseFilename, 1};
16680b57cec5SDimitry Andric   OverlapStats Overlap;
16698bcb0991SDimitry Andric   Error E = Overlap.accumulateCounts(BaseFilename, TestFilename, IsCS);
16700b57cec5SDimitry Andric   if (E)
1671fe6060f1SDimitry Andric     exitWithError(std::move(E), "error in getting profile count sums");
16720b57cec5SDimitry Andric   if (Overlap.Base.CountSum < 1.0f) {
16730b57cec5SDimitry Andric     OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n";
16740b57cec5SDimitry Andric     exit(0);
16750b57cec5SDimitry Andric   }
16760b57cec5SDimitry Andric   if (Overlap.Test.CountSum < 1.0f) {
16770b57cec5SDimitry Andric     OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n";
16780b57cec5SDimitry Andric     exit(0);
16790b57cec5SDimitry Andric   }
168081ad6265SDimitry Andric   loadInput(WeightedInput, nullptr, nullptr, /*ProfiledBinary=*/"", &Context);
16810b57cec5SDimitry Andric   overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS,
16820b57cec5SDimitry Andric                IsCS);
16830b57cec5SDimitry Andric   Overlap.dump(OS);
16840b57cec5SDimitry Andric }
16850b57cec5SDimitry Andric 
1686e8d8bef9SDimitry Andric namespace {
1687e8d8bef9SDimitry Andric struct SampleOverlapStats {
1688349cc55cSDimitry Andric   SampleContext BaseName;
1689349cc55cSDimitry Andric   SampleContext TestName;
1690e8d8bef9SDimitry Andric   // Number of overlap units
16915f757f3fSDimitry Andric   uint64_t OverlapCount = 0;
1692e8d8bef9SDimitry Andric   // Total samples of overlap units
16935f757f3fSDimitry Andric   uint64_t OverlapSample = 0;
1694e8d8bef9SDimitry Andric   // Number of and total samples of units that only present in base or test
1695e8d8bef9SDimitry Andric   // profile
16965f757f3fSDimitry Andric   uint64_t BaseUniqueCount = 0;
16975f757f3fSDimitry Andric   uint64_t BaseUniqueSample = 0;
16985f757f3fSDimitry Andric   uint64_t TestUniqueCount = 0;
16995f757f3fSDimitry Andric   uint64_t TestUniqueSample = 0;
1700e8d8bef9SDimitry Andric   // Number of units and total samples in base or test profile
17015f757f3fSDimitry Andric   uint64_t BaseCount = 0;
17025f757f3fSDimitry Andric   uint64_t BaseSample = 0;
17035f757f3fSDimitry Andric   uint64_t TestCount = 0;
17045f757f3fSDimitry Andric   uint64_t TestSample = 0;
1705e8d8bef9SDimitry Andric   // Number of and total samples of units that present in at least one profile
17065f757f3fSDimitry Andric   uint64_t UnionCount = 0;
17075f757f3fSDimitry Andric   uint64_t UnionSample = 0;
1708e8d8bef9SDimitry Andric   // Weighted similarity
17095f757f3fSDimitry Andric   double Similarity = 0.0;
1710e8d8bef9SDimitry Andric   // For SampleOverlapStats instances representing functions, weights of the
1711e8d8bef9SDimitry Andric   // function in base and test profiles
17125f757f3fSDimitry Andric   double BaseWeight = 0.0;
17135f757f3fSDimitry Andric   double TestWeight = 0.0;
1714e8d8bef9SDimitry Andric 
17155f757f3fSDimitry Andric   SampleOverlapStats() = default;
1716e8d8bef9SDimitry Andric };
1717e8d8bef9SDimitry Andric } // end anonymous namespace
1718e8d8bef9SDimitry Andric 
1719e8d8bef9SDimitry Andric namespace {
1720e8d8bef9SDimitry Andric struct FuncSampleStats {
1721cb14a3feSDimitry Andric   uint64_t SampleSum = 0;
1722cb14a3feSDimitry Andric   uint64_t MaxSample = 0;
1723cb14a3feSDimitry Andric   uint64_t HotBlockCount = 0;
1724cb14a3feSDimitry Andric   FuncSampleStats() = default;
FuncSampleStats__anonae06d1131011::FuncSampleStats1725e8d8bef9SDimitry Andric   FuncSampleStats(uint64_t SampleSum, uint64_t MaxSample,
1726e8d8bef9SDimitry Andric                   uint64_t HotBlockCount)
1727e8d8bef9SDimitry Andric       : SampleSum(SampleSum), MaxSample(MaxSample),
1728e8d8bef9SDimitry Andric         HotBlockCount(HotBlockCount) {}
1729e8d8bef9SDimitry Andric };
1730e8d8bef9SDimitry Andric } // end anonymous namespace
1731e8d8bef9SDimitry Andric 
1732e8d8bef9SDimitry Andric namespace {
1733e8d8bef9SDimitry Andric enum MatchStatus { MS_Match, MS_FirstUnique, MS_SecondUnique, MS_None };
1734e8d8bef9SDimitry Andric 
1735e8d8bef9SDimitry Andric // Class for updating merging steps for two sorted maps. The class should be
1736e8d8bef9SDimitry Andric // instantiated with a map iterator type.
1737e8d8bef9SDimitry Andric template <class T> class MatchStep {
1738e8d8bef9SDimitry Andric public:
1739e8d8bef9SDimitry Andric   MatchStep() = delete;
1740e8d8bef9SDimitry Andric 
MatchStep(T FirstIter,T FirstEnd,T SecondIter,T SecondEnd)1741e8d8bef9SDimitry Andric   MatchStep(T FirstIter, T FirstEnd, T SecondIter, T SecondEnd)
1742e8d8bef9SDimitry Andric       : FirstIter(FirstIter), FirstEnd(FirstEnd), SecondIter(SecondIter),
1743e8d8bef9SDimitry Andric         SecondEnd(SecondEnd), Status(MS_None) {}
1744e8d8bef9SDimitry Andric 
areBothFinished() const1745e8d8bef9SDimitry Andric   bool areBothFinished() const {
1746e8d8bef9SDimitry Andric     return (FirstIter == FirstEnd && SecondIter == SecondEnd);
1747e8d8bef9SDimitry Andric   }
1748e8d8bef9SDimitry Andric 
isFirstFinished() const1749e8d8bef9SDimitry Andric   bool isFirstFinished() const { return FirstIter == FirstEnd; }
1750e8d8bef9SDimitry Andric 
isSecondFinished() const1751e8d8bef9SDimitry Andric   bool isSecondFinished() const { return SecondIter == SecondEnd; }
1752e8d8bef9SDimitry Andric 
1753e8d8bef9SDimitry Andric   /// Advance one step based on the previous match status unless the previous
1754e8d8bef9SDimitry Andric   /// status is MS_None. Then update Status based on the comparison between two
1755e8d8bef9SDimitry Andric   /// container iterators at the current step. If the previous status is
1756e8d8bef9SDimitry Andric   /// MS_None, it means two iterators are at the beginning and no comparison has
1757e8d8bef9SDimitry Andric   /// been made, so we simply update Status without advancing the iterators.
1758e8d8bef9SDimitry Andric   void updateOneStep();
1759e8d8bef9SDimitry Andric 
getFirstIter() const1760e8d8bef9SDimitry Andric   T getFirstIter() const { return FirstIter; }
1761e8d8bef9SDimitry Andric 
getSecondIter() const1762e8d8bef9SDimitry Andric   T getSecondIter() const { return SecondIter; }
1763e8d8bef9SDimitry Andric 
getMatchStatus() const1764e8d8bef9SDimitry Andric   MatchStatus getMatchStatus() const { return Status; }
1765e8d8bef9SDimitry Andric 
1766e8d8bef9SDimitry Andric private:
1767e8d8bef9SDimitry Andric   // Current iterator and end iterator of the first container.
1768e8d8bef9SDimitry Andric   T FirstIter;
1769e8d8bef9SDimitry Andric   T FirstEnd;
1770e8d8bef9SDimitry Andric   // Current iterator and end iterator of the second container.
1771e8d8bef9SDimitry Andric   T SecondIter;
1772e8d8bef9SDimitry Andric   T SecondEnd;
1773e8d8bef9SDimitry Andric   // Match status of the current step.
1774e8d8bef9SDimitry Andric   MatchStatus Status;
1775e8d8bef9SDimitry Andric };
1776e8d8bef9SDimitry Andric } // end anonymous namespace
1777e8d8bef9SDimitry Andric 
updateOneStep()1778e8d8bef9SDimitry Andric template <class T> void MatchStep<T>::updateOneStep() {
1779e8d8bef9SDimitry Andric   switch (Status) {
1780e8d8bef9SDimitry Andric   case MS_Match:
1781e8d8bef9SDimitry Andric     ++FirstIter;
1782e8d8bef9SDimitry Andric     ++SecondIter;
1783e8d8bef9SDimitry Andric     break;
1784e8d8bef9SDimitry Andric   case MS_FirstUnique:
1785e8d8bef9SDimitry Andric     ++FirstIter;
1786e8d8bef9SDimitry Andric     break;
1787e8d8bef9SDimitry Andric   case MS_SecondUnique:
1788e8d8bef9SDimitry Andric     ++SecondIter;
1789e8d8bef9SDimitry Andric     break;
1790e8d8bef9SDimitry Andric   case MS_None:
1791e8d8bef9SDimitry Andric     break;
1792e8d8bef9SDimitry Andric   }
1793e8d8bef9SDimitry Andric 
1794e8d8bef9SDimitry Andric   // Update Status according to iterators at the current step.
1795e8d8bef9SDimitry Andric   if (areBothFinished())
1796e8d8bef9SDimitry Andric     return;
1797e8d8bef9SDimitry Andric   if (FirstIter != FirstEnd &&
1798e8d8bef9SDimitry Andric       (SecondIter == SecondEnd || FirstIter->first < SecondIter->first))
1799e8d8bef9SDimitry Andric     Status = MS_FirstUnique;
1800e8d8bef9SDimitry Andric   else if (SecondIter != SecondEnd &&
1801e8d8bef9SDimitry Andric            (FirstIter == FirstEnd || SecondIter->first < FirstIter->first))
1802e8d8bef9SDimitry Andric     Status = MS_SecondUnique;
1803e8d8bef9SDimitry Andric   else
1804e8d8bef9SDimitry Andric     Status = MS_Match;
1805e8d8bef9SDimitry Andric }
1806e8d8bef9SDimitry Andric 
1807e8d8bef9SDimitry Andric // Return the sum of line/block samples, the max line/block sample, and the
1808e8d8bef9SDimitry Andric // number of line/block samples above the given threshold in a function
1809e8d8bef9SDimitry Andric // including its inlinees.
getFuncSampleStats(const sampleprof::FunctionSamples & Func,FuncSampleStats & FuncStats,uint64_t HotThreshold)1810e8d8bef9SDimitry Andric static void getFuncSampleStats(const sampleprof::FunctionSamples &Func,
1811e8d8bef9SDimitry Andric                                FuncSampleStats &FuncStats,
1812e8d8bef9SDimitry Andric                                uint64_t HotThreshold) {
1813e8d8bef9SDimitry Andric   for (const auto &L : Func.getBodySamples()) {
1814e8d8bef9SDimitry Andric     uint64_t Sample = L.second.getSamples();
1815e8d8bef9SDimitry Andric     FuncStats.SampleSum += Sample;
1816e8d8bef9SDimitry Andric     FuncStats.MaxSample = std::max(FuncStats.MaxSample, Sample);
1817e8d8bef9SDimitry Andric     if (Sample >= HotThreshold)
1818e8d8bef9SDimitry Andric       ++FuncStats.HotBlockCount;
1819e8d8bef9SDimitry Andric   }
1820e8d8bef9SDimitry Andric 
1821e8d8bef9SDimitry Andric   for (const auto &C : Func.getCallsiteSamples()) {
1822e8d8bef9SDimitry Andric     for (const auto &F : C.second)
1823e8d8bef9SDimitry Andric       getFuncSampleStats(F.second, FuncStats, HotThreshold);
1824e8d8bef9SDimitry Andric   }
1825e8d8bef9SDimitry Andric }
1826e8d8bef9SDimitry Andric 
1827e8d8bef9SDimitry Andric /// Predicate that determines if a function is hot with a given threshold. We
1828e8d8bef9SDimitry Andric /// keep it separate from its callsites for possible extension in the future.
isFunctionHot(const FuncSampleStats & FuncStats,uint64_t HotThreshold)1829e8d8bef9SDimitry Andric static bool isFunctionHot(const FuncSampleStats &FuncStats,
1830e8d8bef9SDimitry Andric                           uint64_t HotThreshold) {
1831e8d8bef9SDimitry Andric   // We intentionally compare the maximum sample count in a function with the
1832e8d8bef9SDimitry Andric   // HotThreshold to get an approximate determination on hot functions.
1833e8d8bef9SDimitry Andric   return (FuncStats.MaxSample >= HotThreshold);
1834e8d8bef9SDimitry Andric }
1835e8d8bef9SDimitry Andric 
1836e8d8bef9SDimitry Andric namespace {
1837e8d8bef9SDimitry Andric class SampleOverlapAggregator {
1838e8d8bef9SDimitry Andric public:
SampleOverlapAggregator(const std::string & BaseFilename,const std::string & TestFilename,double LowSimilarityThreshold,double Epsilon,const OverlapFuncFilters & FuncFilter)1839e8d8bef9SDimitry Andric   SampleOverlapAggregator(const std::string &BaseFilename,
1840e8d8bef9SDimitry Andric                           const std::string &TestFilename,
1841e8d8bef9SDimitry Andric                           double LowSimilarityThreshold, double Epsilon,
1842e8d8bef9SDimitry Andric                           const OverlapFuncFilters &FuncFilter)
1843e8d8bef9SDimitry Andric       : BaseFilename(BaseFilename), TestFilename(TestFilename),
1844e8d8bef9SDimitry Andric         LowSimilarityThreshold(LowSimilarityThreshold), Epsilon(Epsilon),
1845e8d8bef9SDimitry Andric         FuncFilter(FuncFilter) {}
1846e8d8bef9SDimitry Andric 
1847e8d8bef9SDimitry Andric   /// Detect 0-sample input profile and report to output stream. This interface
1848e8d8bef9SDimitry Andric   /// should be called after loadProfiles().
1849e8d8bef9SDimitry Andric   bool detectZeroSampleProfile(raw_fd_ostream &OS) const;
1850e8d8bef9SDimitry Andric 
1851e8d8bef9SDimitry Andric   /// Write out function-level similarity statistics for functions specified by
1852e8d8bef9SDimitry Andric   /// options --function, --value-cutoff, and --similarity-cutoff.
1853e8d8bef9SDimitry Andric   void dumpFuncSimilarity(raw_fd_ostream &OS) const;
1854e8d8bef9SDimitry Andric 
1855e8d8bef9SDimitry Andric   /// Write out program-level similarity and overlap statistics.
1856e8d8bef9SDimitry Andric   void dumpProgramSummary(raw_fd_ostream &OS) const;
1857e8d8bef9SDimitry Andric 
1858e8d8bef9SDimitry Andric   /// Write out hot-function and hot-block statistics for base_profile,
1859e8d8bef9SDimitry Andric   /// test_profile, and their overlap. For both cases, the overlap HO is
1860e8d8bef9SDimitry Andric   /// calculated as follows:
1861e8d8bef9SDimitry Andric   ///    Given the number of functions (or blocks) that are hot in both profiles
1862e8d8bef9SDimitry Andric   ///    HCommon and the number of functions (or blocks) that are hot in at
1863e8d8bef9SDimitry Andric   ///    least one profile HUnion, HO = HCommon / HUnion.
1864e8d8bef9SDimitry Andric   void dumpHotFuncAndBlockOverlap(raw_fd_ostream &OS) const;
1865e8d8bef9SDimitry Andric 
1866e8d8bef9SDimitry Andric   /// This function tries matching functions in base and test profiles. For each
1867e8d8bef9SDimitry Andric   /// pair of matched functions, it aggregates the function-level
1868e8d8bef9SDimitry Andric   /// similarity into a profile-level similarity. It also dump function-level
1869e8d8bef9SDimitry Andric   /// similarity information of functions specified by --function,
1870e8d8bef9SDimitry Andric   /// --value-cutoff, and --similarity-cutoff options. The program-level
1871e8d8bef9SDimitry Andric   /// similarity PS is computed as follows:
1872e8d8bef9SDimitry Andric   ///     Given function-level similarity FS(A) for all function A, the
1873e8d8bef9SDimitry Andric   ///     weight of function A in base profile WB(A), and the weight of function
1874e8d8bef9SDimitry Andric   ///     A in test profile WT(A), compute PS(base_profile, test_profile) =
1875e8d8bef9SDimitry Andric   ///     sum_A(FS(A) * avg(WB(A), WT(A))) ranging in [0.0f to 1.0f] with 0.0
1876e8d8bef9SDimitry Andric   ///     meaning no-overlap.
1877e8d8bef9SDimitry Andric   void computeSampleProfileOverlap(raw_fd_ostream &OS);
1878e8d8bef9SDimitry Andric 
1879e8d8bef9SDimitry Andric   /// Initialize ProfOverlap with the sum of samples in base and test
1880e8d8bef9SDimitry Andric   /// profiles. This function also computes and keeps the sum of samples and
1881e8d8bef9SDimitry Andric   /// max sample counts of each function in BaseStats and TestStats for later
1882e8d8bef9SDimitry Andric   /// use to avoid re-computations.
1883e8d8bef9SDimitry Andric   void initializeSampleProfileOverlap();
1884e8d8bef9SDimitry Andric 
1885e8d8bef9SDimitry Andric   /// Load profiles specified by BaseFilename and TestFilename.
1886e8d8bef9SDimitry Andric   std::error_code loadProfiles();
1887e8d8bef9SDimitry Andric 
1888349cc55cSDimitry Andric   using FuncSampleStatsMap =
1889349cc55cSDimitry Andric       std::unordered_map<SampleContext, FuncSampleStats, SampleContext::Hash>;
1890349cc55cSDimitry Andric 
1891e8d8bef9SDimitry Andric private:
1892e8d8bef9SDimitry Andric   SampleOverlapStats ProfOverlap;
1893e8d8bef9SDimitry Andric   SampleOverlapStats HotFuncOverlap;
1894e8d8bef9SDimitry Andric   SampleOverlapStats HotBlockOverlap;
1895e8d8bef9SDimitry Andric   std::string BaseFilename;
1896e8d8bef9SDimitry Andric   std::string TestFilename;
1897e8d8bef9SDimitry Andric   std::unique_ptr<sampleprof::SampleProfileReader> BaseReader;
1898e8d8bef9SDimitry Andric   std::unique_ptr<sampleprof::SampleProfileReader> TestReader;
1899e8d8bef9SDimitry Andric   // BaseStats and TestStats hold FuncSampleStats for each function, with
1900e8d8bef9SDimitry Andric   // function name as the key.
1901349cc55cSDimitry Andric   FuncSampleStatsMap BaseStats;
1902349cc55cSDimitry Andric   FuncSampleStatsMap TestStats;
1903e8d8bef9SDimitry Andric   // Low similarity threshold in floating point number
1904e8d8bef9SDimitry Andric   double LowSimilarityThreshold;
1905e8d8bef9SDimitry Andric   // Block samples above BaseHotThreshold or TestHotThreshold are considered hot
1906e8d8bef9SDimitry Andric   // for tracking hot blocks.
1907e8d8bef9SDimitry Andric   uint64_t BaseHotThreshold;
1908e8d8bef9SDimitry Andric   uint64_t TestHotThreshold;
1909e8d8bef9SDimitry Andric   // A small threshold used to round the results of floating point accumulations
1910e8d8bef9SDimitry Andric   // to resolve imprecision.
1911e8d8bef9SDimitry Andric   const double Epsilon;
1912e8d8bef9SDimitry Andric   std::multimap<double, SampleOverlapStats, std::greater<double>>
1913e8d8bef9SDimitry Andric       FuncSimilarityDump;
1914e8d8bef9SDimitry Andric   // FuncFilter carries specifications in options --value-cutoff and
1915e8d8bef9SDimitry Andric   // --function.
1916e8d8bef9SDimitry Andric   OverlapFuncFilters FuncFilter;
1917e8d8bef9SDimitry Andric   // Column offsets for printing the function-level details table.
1918e8d8bef9SDimitry Andric   static const unsigned int TestWeightCol = 15;
1919e8d8bef9SDimitry Andric   static const unsigned int SimilarityCol = 30;
1920e8d8bef9SDimitry Andric   static const unsigned int OverlapCol = 43;
1921e8d8bef9SDimitry Andric   static const unsigned int BaseUniqueCol = 53;
1922e8d8bef9SDimitry Andric   static const unsigned int TestUniqueCol = 67;
1923e8d8bef9SDimitry Andric   static const unsigned int BaseSampleCol = 81;
1924e8d8bef9SDimitry Andric   static const unsigned int TestSampleCol = 96;
1925e8d8bef9SDimitry Andric   static const unsigned int FuncNameCol = 111;
1926e8d8bef9SDimitry Andric 
1927e8d8bef9SDimitry Andric   /// Return a similarity of two line/block sample counters in the same
1928e8d8bef9SDimitry Andric   /// function in base and test profiles. The line/block-similarity BS(i) is
1929e8d8bef9SDimitry Andric   /// computed as follows:
1930e8d8bef9SDimitry Andric   ///    For an offsets i, given the sample count at i in base profile BB(i),
1931e8d8bef9SDimitry Andric   ///    the sample count at i in test profile BT(i), the sum of sample counts
1932e8d8bef9SDimitry Andric   ///    in this function in base profile SB, and the sum of sample counts in
1933e8d8bef9SDimitry Andric   ///    this function in test profile ST, compute BS(i) = 1.0 - fabs(BB(i)/SB -
1934e8d8bef9SDimitry Andric   ///    BT(i)/ST), ranging in [0.0f to 1.0f] with 0.0 meaning no-overlap.
1935e8d8bef9SDimitry Andric   double computeBlockSimilarity(uint64_t BaseSample, uint64_t TestSample,
1936e8d8bef9SDimitry Andric                                 const SampleOverlapStats &FuncOverlap) const;
1937e8d8bef9SDimitry Andric 
1938e8d8bef9SDimitry Andric   void updateHotBlockOverlap(uint64_t BaseSample, uint64_t TestSample,
1939e8d8bef9SDimitry Andric                              uint64_t HotBlockCount);
1940e8d8bef9SDimitry Andric 
1941349cc55cSDimitry Andric   void getHotFunctions(const FuncSampleStatsMap &ProfStats,
1942349cc55cSDimitry Andric                        FuncSampleStatsMap &HotFunc,
1943e8d8bef9SDimitry Andric                        uint64_t HotThreshold) const;
1944e8d8bef9SDimitry Andric 
1945e8d8bef9SDimitry Andric   void computeHotFuncOverlap();
1946e8d8bef9SDimitry Andric 
1947e8d8bef9SDimitry Andric   /// This function updates statistics in FuncOverlap, HotBlockOverlap, and
1948e8d8bef9SDimitry Andric   /// Difference for two sample units in a matched function according to the
1949e8d8bef9SDimitry Andric   /// given match status.
1950e8d8bef9SDimitry Andric   void updateOverlapStatsForFunction(uint64_t BaseSample, uint64_t TestSample,
1951e8d8bef9SDimitry Andric                                      uint64_t HotBlockCount,
1952e8d8bef9SDimitry Andric                                      SampleOverlapStats &FuncOverlap,
1953e8d8bef9SDimitry Andric                                      double &Difference, MatchStatus Status);
1954e8d8bef9SDimitry Andric 
1955e8d8bef9SDimitry Andric   /// This function updates statistics in FuncOverlap, HotBlockOverlap, and
1956e8d8bef9SDimitry Andric   /// Difference for unmatched callees that only present in one profile in a
1957e8d8bef9SDimitry Andric   /// matched caller function.
1958e8d8bef9SDimitry Andric   void updateForUnmatchedCallee(const sampleprof::FunctionSamples &Func,
1959e8d8bef9SDimitry Andric                                 SampleOverlapStats &FuncOverlap,
1960e8d8bef9SDimitry Andric                                 double &Difference, MatchStatus Status);
1961e8d8bef9SDimitry Andric 
1962e8d8bef9SDimitry Andric   /// This function updates sample overlap statistics of an overlap function in
1963e8d8bef9SDimitry Andric   /// base and test profile. It also calculates a function-internal similarity
1964e8d8bef9SDimitry Andric   /// FIS as follows:
1965e8d8bef9SDimitry Andric   ///    For offsets i that have samples in at least one profile in this
1966e8d8bef9SDimitry Andric   ///    function A, given BS(i) returned by computeBlockSimilarity(), compute
1967e8d8bef9SDimitry Andric   ///    FIS(A) = (2.0 - sum_i(1.0 - BS(i))) / 2, ranging in [0.0f to 1.0f] with
1968e8d8bef9SDimitry Andric   ///    0.0 meaning no overlap.
1969e8d8bef9SDimitry Andric   double computeSampleFunctionInternalOverlap(
1970e8d8bef9SDimitry Andric       const sampleprof::FunctionSamples &BaseFunc,
1971e8d8bef9SDimitry Andric       const sampleprof::FunctionSamples &TestFunc,
1972e8d8bef9SDimitry Andric       SampleOverlapStats &FuncOverlap);
1973e8d8bef9SDimitry Andric 
1974e8d8bef9SDimitry Andric   /// Function-level similarity (FS) is a weighted value over function internal
1975e8d8bef9SDimitry Andric   /// similarity (FIS). This function computes a function's FS from its FIS by
1976e8d8bef9SDimitry Andric   /// applying the weight.
1977e8d8bef9SDimitry Andric   double weightForFuncSimilarity(double FuncSimilarity, uint64_t BaseFuncSample,
1978e8d8bef9SDimitry Andric                                  uint64_t TestFuncSample) const;
1979e8d8bef9SDimitry Andric 
1980e8d8bef9SDimitry Andric   /// The function-level similarity FS(A) for a function A is computed as
1981e8d8bef9SDimitry Andric   /// follows:
1982e8d8bef9SDimitry Andric   ///     Compute a function-internal similarity FIS(A) by
1983e8d8bef9SDimitry Andric   ///     computeSampleFunctionInternalOverlap(). Then, with the weight of
1984e8d8bef9SDimitry Andric   ///     function A in base profile WB(A), and the weight of function A in test
1985e8d8bef9SDimitry Andric   ///     profile WT(A), compute FS(A) = FIS(A) * (1.0 - fabs(WB(A) - WT(A)))
1986e8d8bef9SDimitry Andric   ///     ranging in [0.0f to 1.0f] with 0.0 meaning no overlap.
1987e8d8bef9SDimitry Andric   double
1988e8d8bef9SDimitry Andric   computeSampleFunctionOverlap(const sampleprof::FunctionSamples *BaseFunc,
1989e8d8bef9SDimitry Andric                                const sampleprof::FunctionSamples *TestFunc,
1990e8d8bef9SDimitry Andric                                SampleOverlapStats *FuncOverlap,
1991e8d8bef9SDimitry Andric                                uint64_t BaseFuncSample,
1992e8d8bef9SDimitry Andric                                uint64_t TestFuncSample);
1993e8d8bef9SDimitry Andric 
1994e8d8bef9SDimitry Andric   /// Profile-level similarity (PS) is a weighted aggregate over function-level
1995e8d8bef9SDimitry Andric   /// similarities (FS). This method weights the FS value by the function
1996e8d8bef9SDimitry Andric   /// weights in the base and test profiles for the aggregation.
1997e8d8bef9SDimitry Andric   double weightByImportance(double FuncSimilarity, uint64_t BaseFuncSample,
1998e8d8bef9SDimitry Andric                             uint64_t TestFuncSample) const;
1999e8d8bef9SDimitry Andric };
2000e8d8bef9SDimitry Andric } // end anonymous namespace
2001e8d8bef9SDimitry Andric 
detectZeroSampleProfile(raw_fd_ostream & OS) const2002e8d8bef9SDimitry Andric bool SampleOverlapAggregator::detectZeroSampleProfile(
2003e8d8bef9SDimitry Andric     raw_fd_ostream &OS) const {
2004e8d8bef9SDimitry Andric   bool HaveZeroSample = false;
2005e8d8bef9SDimitry Andric   if (ProfOverlap.BaseSample == 0) {
2006e8d8bef9SDimitry Andric     OS << "Sum of sample counts for profile " << BaseFilename << " is 0.\n";
2007e8d8bef9SDimitry Andric     HaveZeroSample = true;
2008e8d8bef9SDimitry Andric   }
2009e8d8bef9SDimitry Andric   if (ProfOverlap.TestSample == 0) {
2010e8d8bef9SDimitry Andric     OS << "Sum of sample counts for profile " << TestFilename << " is 0.\n";
2011e8d8bef9SDimitry Andric     HaveZeroSample = true;
2012e8d8bef9SDimitry Andric   }
2013e8d8bef9SDimitry Andric   return HaveZeroSample;
2014e8d8bef9SDimitry Andric }
2015e8d8bef9SDimitry Andric 
computeBlockSimilarity(uint64_t BaseSample,uint64_t TestSample,const SampleOverlapStats & FuncOverlap) const2016e8d8bef9SDimitry Andric double SampleOverlapAggregator::computeBlockSimilarity(
2017e8d8bef9SDimitry Andric     uint64_t BaseSample, uint64_t TestSample,
2018e8d8bef9SDimitry Andric     const SampleOverlapStats &FuncOverlap) const {
2019e8d8bef9SDimitry Andric   double BaseFrac = 0.0;
2020e8d8bef9SDimitry Andric   double TestFrac = 0.0;
2021e8d8bef9SDimitry Andric   if (FuncOverlap.BaseSample > 0)
2022e8d8bef9SDimitry Andric     BaseFrac = static_cast<double>(BaseSample) / FuncOverlap.BaseSample;
2023e8d8bef9SDimitry Andric   if (FuncOverlap.TestSample > 0)
2024e8d8bef9SDimitry Andric     TestFrac = static_cast<double>(TestSample) / FuncOverlap.TestSample;
2025e8d8bef9SDimitry Andric   return 1.0 - std::fabs(BaseFrac - TestFrac);
2026e8d8bef9SDimitry Andric }
2027e8d8bef9SDimitry Andric 
updateHotBlockOverlap(uint64_t BaseSample,uint64_t TestSample,uint64_t HotBlockCount)2028e8d8bef9SDimitry Andric void SampleOverlapAggregator::updateHotBlockOverlap(uint64_t BaseSample,
2029e8d8bef9SDimitry Andric                                                     uint64_t TestSample,
2030e8d8bef9SDimitry Andric                                                     uint64_t HotBlockCount) {
2031e8d8bef9SDimitry Andric   bool IsBaseHot = (BaseSample >= BaseHotThreshold);
2032e8d8bef9SDimitry Andric   bool IsTestHot = (TestSample >= TestHotThreshold);
2033e8d8bef9SDimitry Andric   if (!IsBaseHot && !IsTestHot)
2034e8d8bef9SDimitry Andric     return;
2035e8d8bef9SDimitry Andric 
2036e8d8bef9SDimitry Andric   HotBlockOverlap.UnionCount += HotBlockCount;
2037e8d8bef9SDimitry Andric   if (IsBaseHot)
2038e8d8bef9SDimitry Andric     HotBlockOverlap.BaseCount += HotBlockCount;
2039e8d8bef9SDimitry Andric   if (IsTestHot)
2040e8d8bef9SDimitry Andric     HotBlockOverlap.TestCount += HotBlockCount;
2041e8d8bef9SDimitry Andric   if (IsBaseHot && IsTestHot)
2042e8d8bef9SDimitry Andric     HotBlockOverlap.OverlapCount += HotBlockCount;
2043e8d8bef9SDimitry Andric }
2044e8d8bef9SDimitry Andric 
getHotFunctions(const FuncSampleStatsMap & ProfStats,FuncSampleStatsMap & HotFunc,uint64_t HotThreshold) const2045e8d8bef9SDimitry Andric void SampleOverlapAggregator::getHotFunctions(
2046349cc55cSDimitry Andric     const FuncSampleStatsMap &ProfStats, FuncSampleStatsMap &HotFunc,
2047349cc55cSDimitry Andric     uint64_t HotThreshold) const {
2048e8d8bef9SDimitry Andric   for (const auto &F : ProfStats) {
2049e8d8bef9SDimitry Andric     if (isFunctionHot(F.second, HotThreshold))
2050349cc55cSDimitry Andric       HotFunc.emplace(F.first, F.second);
2051e8d8bef9SDimitry Andric   }
2052e8d8bef9SDimitry Andric }
2053e8d8bef9SDimitry Andric 
computeHotFuncOverlap()2054e8d8bef9SDimitry Andric void SampleOverlapAggregator::computeHotFuncOverlap() {
2055349cc55cSDimitry Andric   FuncSampleStatsMap BaseHotFunc;
2056e8d8bef9SDimitry Andric   getHotFunctions(BaseStats, BaseHotFunc, BaseHotThreshold);
2057e8d8bef9SDimitry Andric   HotFuncOverlap.BaseCount = BaseHotFunc.size();
2058e8d8bef9SDimitry Andric 
2059349cc55cSDimitry Andric   FuncSampleStatsMap TestHotFunc;
2060e8d8bef9SDimitry Andric   getHotFunctions(TestStats, TestHotFunc, TestHotThreshold);
2061e8d8bef9SDimitry Andric   HotFuncOverlap.TestCount = TestHotFunc.size();
2062e8d8bef9SDimitry Andric   HotFuncOverlap.UnionCount = HotFuncOverlap.TestCount;
2063e8d8bef9SDimitry Andric 
2064e8d8bef9SDimitry Andric   for (const auto &F : BaseHotFunc) {
2065349cc55cSDimitry Andric     if (TestHotFunc.count(F.first))
2066e8d8bef9SDimitry Andric       ++HotFuncOverlap.OverlapCount;
2067e8d8bef9SDimitry Andric     else
2068e8d8bef9SDimitry Andric       ++HotFuncOverlap.UnionCount;
2069e8d8bef9SDimitry Andric   }
2070e8d8bef9SDimitry Andric }
2071e8d8bef9SDimitry Andric 
updateOverlapStatsForFunction(uint64_t BaseSample,uint64_t TestSample,uint64_t HotBlockCount,SampleOverlapStats & FuncOverlap,double & Difference,MatchStatus Status)2072e8d8bef9SDimitry Andric void SampleOverlapAggregator::updateOverlapStatsForFunction(
2073e8d8bef9SDimitry Andric     uint64_t BaseSample, uint64_t TestSample, uint64_t HotBlockCount,
2074e8d8bef9SDimitry Andric     SampleOverlapStats &FuncOverlap, double &Difference, MatchStatus Status) {
2075e8d8bef9SDimitry Andric   assert(Status != MS_None &&
2076e8d8bef9SDimitry Andric          "Match status should be updated before updating overlap statistics");
2077e8d8bef9SDimitry Andric   if (Status == MS_FirstUnique) {
2078e8d8bef9SDimitry Andric     TestSample = 0;
2079e8d8bef9SDimitry Andric     FuncOverlap.BaseUniqueSample += BaseSample;
2080e8d8bef9SDimitry Andric   } else if (Status == MS_SecondUnique) {
2081e8d8bef9SDimitry Andric     BaseSample = 0;
2082e8d8bef9SDimitry Andric     FuncOverlap.TestUniqueSample += TestSample;
2083e8d8bef9SDimitry Andric   } else {
2084e8d8bef9SDimitry Andric     ++FuncOverlap.OverlapCount;
2085e8d8bef9SDimitry Andric   }
2086e8d8bef9SDimitry Andric 
2087e8d8bef9SDimitry Andric   FuncOverlap.UnionSample += std::max(BaseSample, TestSample);
2088e8d8bef9SDimitry Andric   FuncOverlap.OverlapSample += std::min(BaseSample, TestSample);
2089e8d8bef9SDimitry Andric   Difference +=
2090e8d8bef9SDimitry Andric       1.0 - computeBlockSimilarity(BaseSample, TestSample, FuncOverlap);
2091e8d8bef9SDimitry Andric   updateHotBlockOverlap(BaseSample, TestSample, HotBlockCount);
2092e8d8bef9SDimitry Andric }
2093e8d8bef9SDimitry Andric 
updateForUnmatchedCallee(const sampleprof::FunctionSamples & Func,SampleOverlapStats & FuncOverlap,double & Difference,MatchStatus Status)2094e8d8bef9SDimitry Andric void SampleOverlapAggregator::updateForUnmatchedCallee(
2095e8d8bef9SDimitry Andric     const sampleprof::FunctionSamples &Func, SampleOverlapStats &FuncOverlap,
2096e8d8bef9SDimitry Andric     double &Difference, MatchStatus Status) {
2097e8d8bef9SDimitry Andric   assert((Status == MS_FirstUnique || Status == MS_SecondUnique) &&
2098e8d8bef9SDimitry Andric          "Status must be either of the two unmatched cases");
2099e8d8bef9SDimitry Andric   FuncSampleStats FuncStats;
2100e8d8bef9SDimitry Andric   if (Status == MS_FirstUnique) {
2101e8d8bef9SDimitry Andric     getFuncSampleStats(Func, FuncStats, BaseHotThreshold);
2102e8d8bef9SDimitry Andric     updateOverlapStatsForFunction(FuncStats.SampleSum, 0,
2103e8d8bef9SDimitry Andric                                   FuncStats.HotBlockCount, FuncOverlap,
2104e8d8bef9SDimitry Andric                                   Difference, Status);
2105e8d8bef9SDimitry Andric   } else {
2106e8d8bef9SDimitry Andric     getFuncSampleStats(Func, FuncStats, TestHotThreshold);
2107e8d8bef9SDimitry Andric     updateOverlapStatsForFunction(0, FuncStats.SampleSum,
2108e8d8bef9SDimitry Andric                                   FuncStats.HotBlockCount, FuncOverlap,
2109e8d8bef9SDimitry Andric                                   Difference, Status);
2110e8d8bef9SDimitry Andric   }
2111e8d8bef9SDimitry Andric }
2112e8d8bef9SDimitry Andric 
computeSampleFunctionInternalOverlap(const sampleprof::FunctionSamples & BaseFunc,const sampleprof::FunctionSamples & TestFunc,SampleOverlapStats & FuncOverlap)2113e8d8bef9SDimitry Andric double SampleOverlapAggregator::computeSampleFunctionInternalOverlap(
2114e8d8bef9SDimitry Andric     const sampleprof::FunctionSamples &BaseFunc,
2115e8d8bef9SDimitry Andric     const sampleprof::FunctionSamples &TestFunc,
2116e8d8bef9SDimitry Andric     SampleOverlapStats &FuncOverlap) {
2117e8d8bef9SDimitry Andric 
2118e8d8bef9SDimitry Andric   using namespace sampleprof;
2119e8d8bef9SDimitry Andric 
2120e8d8bef9SDimitry Andric   double Difference = 0;
2121e8d8bef9SDimitry Andric 
2122e8d8bef9SDimitry Andric   // Accumulate Difference for regular line/block samples in the function.
2123e8d8bef9SDimitry Andric   // We match them through sort-merge join algorithm because
2124e8d8bef9SDimitry Andric   // FunctionSamples::getBodySamples() returns a map of sample counters ordered
2125e8d8bef9SDimitry Andric   // by their offsets.
2126e8d8bef9SDimitry Andric   MatchStep<BodySampleMap::const_iterator> BlockIterStep(
2127e8d8bef9SDimitry Andric       BaseFunc.getBodySamples().cbegin(), BaseFunc.getBodySamples().cend(),
2128e8d8bef9SDimitry Andric       TestFunc.getBodySamples().cbegin(), TestFunc.getBodySamples().cend());
2129e8d8bef9SDimitry Andric   BlockIterStep.updateOneStep();
2130e8d8bef9SDimitry Andric   while (!BlockIterStep.areBothFinished()) {
2131e8d8bef9SDimitry Andric     uint64_t BaseSample =
2132e8d8bef9SDimitry Andric         BlockIterStep.isFirstFinished()
2133e8d8bef9SDimitry Andric             ? 0
2134e8d8bef9SDimitry Andric             : BlockIterStep.getFirstIter()->second.getSamples();
2135e8d8bef9SDimitry Andric     uint64_t TestSample =
2136e8d8bef9SDimitry Andric         BlockIterStep.isSecondFinished()
2137e8d8bef9SDimitry Andric             ? 0
2138e8d8bef9SDimitry Andric             : BlockIterStep.getSecondIter()->second.getSamples();
2139e8d8bef9SDimitry Andric     updateOverlapStatsForFunction(BaseSample, TestSample, 1, FuncOverlap,
2140e8d8bef9SDimitry Andric                                   Difference, BlockIterStep.getMatchStatus());
2141e8d8bef9SDimitry Andric 
2142e8d8bef9SDimitry Andric     BlockIterStep.updateOneStep();
2143e8d8bef9SDimitry Andric   }
2144e8d8bef9SDimitry Andric 
2145e8d8bef9SDimitry Andric   // Accumulate Difference for callsite lines in the function. We match
2146e8d8bef9SDimitry Andric   // them through sort-merge algorithm because
2147e8d8bef9SDimitry Andric   // FunctionSamples::getCallsiteSamples() returns a map of callsite records
2148e8d8bef9SDimitry Andric   // ordered by their offsets.
2149e8d8bef9SDimitry Andric   MatchStep<CallsiteSampleMap::const_iterator> CallsiteIterStep(
2150e8d8bef9SDimitry Andric       BaseFunc.getCallsiteSamples().cbegin(),
2151e8d8bef9SDimitry Andric       BaseFunc.getCallsiteSamples().cend(),
2152e8d8bef9SDimitry Andric       TestFunc.getCallsiteSamples().cbegin(),
2153e8d8bef9SDimitry Andric       TestFunc.getCallsiteSamples().cend());
2154e8d8bef9SDimitry Andric   CallsiteIterStep.updateOneStep();
2155e8d8bef9SDimitry Andric   while (!CallsiteIterStep.areBothFinished()) {
2156e8d8bef9SDimitry Andric     MatchStatus CallsiteStepStatus = CallsiteIterStep.getMatchStatus();
2157e8d8bef9SDimitry Andric     assert(CallsiteStepStatus != MS_None &&
2158e8d8bef9SDimitry Andric            "Match status should be updated before entering loop body");
2159e8d8bef9SDimitry Andric 
2160e8d8bef9SDimitry Andric     if (CallsiteStepStatus != MS_Match) {
2161e8d8bef9SDimitry Andric       auto Callsite = (CallsiteStepStatus == MS_FirstUnique)
2162e8d8bef9SDimitry Andric                           ? CallsiteIterStep.getFirstIter()
2163e8d8bef9SDimitry Andric                           : CallsiteIterStep.getSecondIter();
2164e8d8bef9SDimitry Andric       for (const auto &F : Callsite->second)
2165e8d8bef9SDimitry Andric         updateForUnmatchedCallee(F.second, FuncOverlap, Difference,
2166e8d8bef9SDimitry Andric                                  CallsiteStepStatus);
2167e8d8bef9SDimitry Andric     } else {
2168e8d8bef9SDimitry Andric       // There may be multiple inlinees at the same offset, so we need to try
2169e8d8bef9SDimitry Andric       // matching all of them. This match is implemented through sort-merge
2170e8d8bef9SDimitry Andric       // algorithm because callsite records at the same offset are ordered by
2171e8d8bef9SDimitry Andric       // function names.
2172e8d8bef9SDimitry Andric       MatchStep<FunctionSamplesMap::const_iterator> CalleeIterStep(
2173e8d8bef9SDimitry Andric           CallsiteIterStep.getFirstIter()->second.cbegin(),
2174e8d8bef9SDimitry Andric           CallsiteIterStep.getFirstIter()->second.cend(),
2175e8d8bef9SDimitry Andric           CallsiteIterStep.getSecondIter()->second.cbegin(),
2176e8d8bef9SDimitry Andric           CallsiteIterStep.getSecondIter()->second.cend());
2177e8d8bef9SDimitry Andric       CalleeIterStep.updateOneStep();
2178e8d8bef9SDimitry Andric       while (!CalleeIterStep.areBothFinished()) {
2179e8d8bef9SDimitry Andric         MatchStatus CalleeStepStatus = CalleeIterStep.getMatchStatus();
2180e8d8bef9SDimitry Andric         if (CalleeStepStatus != MS_Match) {
2181e8d8bef9SDimitry Andric           auto Callee = (CalleeStepStatus == MS_FirstUnique)
2182e8d8bef9SDimitry Andric                             ? CalleeIterStep.getFirstIter()
2183e8d8bef9SDimitry Andric                             : CalleeIterStep.getSecondIter();
2184e8d8bef9SDimitry Andric           updateForUnmatchedCallee(Callee->second, FuncOverlap, Difference,
2185e8d8bef9SDimitry Andric                                    CalleeStepStatus);
2186e8d8bef9SDimitry Andric         } else {
2187e8d8bef9SDimitry Andric           // An inlined function can contain other inlinees inside, so compute
2188e8d8bef9SDimitry Andric           // the Difference recursively.
2189e8d8bef9SDimitry Andric           Difference += 2.0 - 2 * computeSampleFunctionInternalOverlap(
2190e8d8bef9SDimitry Andric                                       CalleeIterStep.getFirstIter()->second,
2191e8d8bef9SDimitry Andric                                       CalleeIterStep.getSecondIter()->second,
2192e8d8bef9SDimitry Andric                                       FuncOverlap);
2193e8d8bef9SDimitry Andric         }
2194e8d8bef9SDimitry Andric         CalleeIterStep.updateOneStep();
2195e8d8bef9SDimitry Andric       }
2196e8d8bef9SDimitry Andric     }
2197e8d8bef9SDimitry Andric     CallsiteIterStep.updateOneStep();
2198e8d8bef9SDimitry Andric   }
2199e8d8bef9SDimitry Andric 
2200e8d8bef9SDimitry Andric   // Difference reflects the total differences of line/block samples in this
2201e8d8bef9SDimitry Andric   // function and ranges in [0.0f to 2.0f]. Take (2.0 - Difference) / 2 to
2202e8d8bef9SDimitry Andric   // reflect the similarity between function profiles in [0.0f to 1.0f].
2203e8d8bef9SDimitry Andric   return (2.0 - Difference) / 2;
2204e8d8bef9SDimitry Andric }
2205e8d8bef9SDimitry Andric 
weightForFuncSimilarity(double FuncInternalSimilarity,uint64_t BaseFuncSample,uint64_t TestFuncSample) const2206e8d8bef9SDimitry Andric double SampleOverlapAggregator::weightForFuncSimilarity(
2207e8d8bef9SDimitry Andric     double FuncInternalSimilarity, uint64_t BaseFuncSample,
2208e8d8bef9SDimitry Andric     uint64_t TestFuncSample) const {
2209e8d8bef9SDimitry Andric   // Compute the weight as the distance between the function weights in two
2210e8d8bef9SDimitry Andric   // profiles.
2211e8d8bef9SDimitry Andric   double BaseFrac = 0.0;
2212e8d8bef9SDimitry Andric   double TestFrac = 0.0;
2213e8d8bef9SDimitry Andric   assert(ProfOverlap.BaseSample > 0 &&
2214e8d8bef9SDimitry Andric          "Total samples in base profile should be greater than 0");
2215e8d8bef9SDimitry Andric   BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample;
2216e8d8bef9SDimitry Andric   assert(ProfOverlap.TestSample > 0 &&
2217e8d8bef9SDimitry Andric          "Total samples in test profile should be greater than 0");
2218e8d8bef9SDimitry Andric   TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample;
2219e8d8bef9SDimitry Andric   double WeightDistance = std::fabs(BaseFrac - TestFrac);
2220e8d8bef9SDimitry Andric 
2221e8d8bef9SDimitry Andric   // Take WeightDistance into the similarity.
2222e8d8bef9SDimitry Andric   return FuncInternalSimilarity * (1 - WeightDistance);
2223e8d8bef9SDimitry Andric }
2224e8d8bef9SDimitry Andric 
2225e8d8bef9SDimitry Andric double
weightByImportance(double FuncSimilarity,uint64_t BaseFuncSample,uint64_t TestFuncSample) const2226e8d8bef9SDimitry Andric SampleOverlapAggregator::weightByImportance(double FuncSimilarity,
2227e8d8bef9SDimitry Andric                                             uint64_t BaseFuncSample,
2228e8d8bef9SDimitry Andric                                             uint64_t TestFuncSample) const {
2229e8d8bef9SDimitry Andric 
2230e8d8bef9SDimitry Andric   double BaseFrac = 0.0;
2231e8d8bef9SDimitry Andric   double TestFrac = 0.0;
2232e8d8bef9SDimitry Andric   assert(ProfOverlap.BaseSample > 0 &&
2233e8d8bef9SDimitry Andric          "Total samples in base profile should be greater than 0");
2234e8d8bef9SDimitry Andric   BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample / 2.0;
2235e8d8bef9SDimitry Andric   assert(ProfOverlap.TestSample > 0 &&
2236e8d8bef9SDimitry Andric          "Total samples in test profile should be greater than 0");
2237e8d8bef9SDimitry Andric   TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample / 2.0;
2238e8d8bef9SDimitry Andric   return FuncSimilarity * (BaseFrac + TestFrac);
2239e8d8bef9SDimitry Andric }
2240e8d8bef9SDimitry Andric 
computeSampleFunctionOverlap(const sampleprof::FunctionSamples * BaseFunc,const sampleprof::FunctionSamples * TestFunc,SampleOverlapStats * FuncOverlap,uint64_t BaseFuncSample,uint64_t TestFuncSample)2241e8d8bef9SDimitry Andric double SampleOverlapAggregator::computeSampleFunctionOverlap(
2242e8d8bef9SDimitry Andric     const sampleprof::FunctionSamples *BaseFunc,
2243e8d8bef9SDimitry Andric     const sampleprof::FunctionSamples *TestFunc,
2244e8d8bef9SDimitry Andric     SampleOverlapStats *FuncOverlap, uint64_t BaseFuncSample,
2245e8d8bef9SDimitry Andric     uint64_t TestFuncSample) {
2246e8d8bef9SDimitry Andric   // Default function internal similarity before weighted, meaning two functions
2247e8d8bef9SDimitry Andric   // has no overlap.
2248e8d8bef9SDimitry Andric   const double DefaultFuncInternalSimilarity = 0;
2249e8d8bef9SDimitry Andric   double FuncSimilarity;
2250e8d8bef9SDimitry Andric   double FuncInternalSimilarity;
2251e8d8bef9SDimitry Andric 
2252e8d8bef9SDimitry Andric   // If BaseFunc or TestFunc is nullptr, it means the functions do not overlap.
2253e8d8bef9SDimitry Andric   // In this case, we use DefaultFuncInternalSimilarity as the function internal
2254e8d8bef9SDimitry Andric   // similarity.
2255e8d8bef9SDimitry Andric   if (!BaseFunc || !TestFunc) {
2256e8d8bef9SDimitry Andric     FuncInternalSimilarity = DefaultFuncInternalSimilarity;
2257e8d8bef9SDimitry Andric   } else {
2258e8d8bef9SDimitry Andric     assert(FuncOverlap != nullptr &&
2259e8d8bef9SDimitry Andric            "FuncOverlap should be provided in this case");
2260e8d8bef9SDimitry Andric     FuncInternalSimilarity = computeSampleFunctionInternalOverlap(
2261e8d8bef9SDimitry Andric         *BaseFunc, *TestFunc, *FuncOverlap);
2262e8d8bef9SDimitry Andric     // Now, FuncInternalSimilarity may be a little less than 0 due to
2263e8d8bef9SDimitry Andric     // imprecision of floating point accumulations. Make it zero if the
2264e8d8bef9SDimitry Andric     // difference is below Epsilon.
2265e8d8bef9SDimitry Andric     FuncInternalSimilarity = (std::fabs(FuncInternalSimilarity - 0) < Epsilon)
2266e8d8bef9SDimitry Andric                                  ? 0
2267e8d8bef9SDimitry Andric                                  : FuncInternalSimilarity;
2268e8d8bef9SDimitry Andric   }
2269e8d8bef9SDimitry Andric   FuncSimilarity = weightForFuncSimilarity(FuncInternalSimilarity,
2270e8d8bef9SDimitry Andric                                            BaseFuncSample, TestFuncSample);
2271e8d8bef9SDimitry Andric   return FuncSimilarity;
2272e8d8bef9SDimitry Andric }
2273e8d8bef9SDimitry Andric 
computeSampleProfileOverlap(raw_fd_ostream & OS)2274e8d8bef9SDimitry Andric void SampleOverlapAggregator::computeSampleProfileOverlap(raw_fd_ostream &OS) {
2275e8d8bef9SDimitry Andric   using namespace sampleprof;
2276e8d8bef9SDimitry Andric 
2277349cc55cSDimitry Andric   std::unordered_map<SampleContext, const FunctionSamples *,
2278349cc55cSDimitry Andric                      SampleContext::Hash>
2279349cc55cSDimitry Andric       BaseFuncProf;
2280e8d8bef9SDimitry Andric   const auto &BaseProfiles = BaseReader->getProfiles();
2281e8d8bef9SDimitry Andric   for (const auto &BaseFunc : BaseProfiles) {
2282349cc55cSDimitry Andric     BaseFuncProf.emplace(BaseFunc.second.getContext(), &(BaseFunc.second));
2283e8d8bef9SDimitry Andric   }
2284e8d8bef9SDimitry Andric   ProfOverlap.UnionCount = BaseFuncProf.size();
2285e8d8bef9SDimitry Andric 
2286e8d8bef9SDimitry Andric   const auto &TestProfiles = TestReader->getProfiles();
2287e8d8bef9SDimitry Andric   for (const auto &TestFunc : TestProfiles) {
2288e8d8bef9SDimitry Andric     SampleOverlapStats FuncOverlap;
2289349cc55cSDimitry Andric     FuncOverlap.TestName = TestFunc.second.getContext();
2290e8d8bef9SDimitry Andric     assert(TestStats.count(FuncOverlap.TestName) &&
2291e8d8bef9SDimitry Andric            "TestStats should have records for all functions in test profile "
2292e8d8bef9SDimitry Andric            "except inlinees");
2293e8d8bef9SDimitry Andric     FuncOverlap.TestSample = TestStats[FuncOverlap.TestName].SampleSum;
2294e8d8bef9SDimitry Andric 
2295349cc55cSDimitry Andric     bool Matched = false;
2296e8d8bef9SDimitry Andric     const auto Match = BaseFuncProf.find(FuncOverlap.TestName);
2297e8d8bef9SDimitry Andric     if (Match == BaseFuncProf.end()) {
2298e8d8bef9SDimitry Andric       const FuncSampleStats &FuncStats = TestStats[FuncOverlap.TestName];
2299e8d8bef9SDimitry Andric       ++ProfOverlap.TestUniqueCount;
2300e8d8bef9SDimitry Andric       ProfOverlap.TestUniqueSample += FuncStats.SampleSum;
2301e8d8bef9SDimitry Andric       FuncOverlap.TestUniqueSample = FuncStats.SampleSum;
2302e8d8bef9SDimitry Andric 
2303e8d8bef9SDimitry Andric       updateHotBlockOverlap(0, FuncStats.SampleSum, FuncStats.HotBlockCount);
2304e8d8bef9SDimitry Andric 
2305e8d8bef9SDimitry Andric       double FuncSimilarity = computeSampleFunctionOverlap(
2306e8d8bef9SDimitry Andric           nullptr, nullptr, nullptr, 0, FuncStats.SampleSum);
2307e8d8bef9SDimitry Andric       ProfOverlap.Similarity +=
2308e8d8bef9SDimitry Andric           weightByImportance(FuncSimilarity, 0, FuncStats.SampleSum);
2309e8d8bef9SDimitry Andric 
2310e8d8bef9SDimitry Andric       ++ProfOverlap.UnionCount;
2311e8d8bef9SDimitry Andric       ProfOverlap.UnionSample += FuncStats.SampleSum;
2312e8d8bef9SDimitry Andric     } else {
2313e8d8bef9SDimitry Andric       ++ProfOverlap.OverlapCount;
2314e8d8bef9SDimitry Andric 
2315e8d8bef9SDimitry Andric       // Two functions match with each other. Compute function-level overlap and
2316e8d8bef9SDimitry Andric       // aggregate them into profile-level overlap.
2317349cc55cSDimitry Andric       FuncOverlap.BaseName = Match->second->getContext();
2318e8d8bef9SDimitry Andric       assert(BaseStats.count(FuncOverlap.BaseName) &&
2319e8d8bef9SDimitry Andric              "BaseStats should have records for all functions in base profile "
2320e8d8bef9SDimitry Andric              "except inlinees");
2321e8d8bef9SDimitry Andric       FuncOverlap.BaseSample = BaseStats[FuncOverlap.BaseName].SampleSum;
2322e8d8bef9SDimitry Andric 
2323e8d8bef9SDimitry Andric       FuncOverlap.Similarity = computeSampleFunctionOverlap(
2324e8d8bef9SDimitry Andric           Match->second, &TestFunc.second, &FuncOverlap, FuncOverlap.BaseSample,
2325e8d8bef9SDimitry Andric           FuncOverlap.TestSample);
2326e8d8bef9SDimitry Andric       ProfOverlap.Similarity +=
2327e8d8bef9SDimitry Andric           weightByImportance(FuncOverlap.Similarity, FuncOverlap.BaseSample,
2328e8d8bef9SDimitry Andric                              FuncOverlap.TestSample);
2329e8d8bef9SDimitry Andric       ProfOverlap.OverlapSample += FuncOverlap.OverlapSample;
2330e8d8bef9SDimitry Andric       ProfOverlap.UnionSample += FuncOverlap.UnionSample;
2331e8d8bef9SDimitry Andric 
2332e8d8bef9SDimitry Andric       // Accumulate the percentage of base unique and test unique samples into
2333e8d8bef9SDimitry Andric       // ProfOverlap.
2334e8d8bef9SDimitry Andric       ProfOverlap.BaseUniqueSample += FuncOverlap.BaseUniqueSample;
2335e8d8bef9SDimitry Andric       ProfOverlap.TestUniqueSample += FuncOverlap.TestUniqueSample;
2336e8d8bef9SDimitry Andric 
2337e8d8bef9SDimitry Andric       // Remove matched base functions for later reporting functions not found
2338e8d8bef9SDimitry Andric       // in test profile.
2339e8d8bef9SDimitry Andric       BaseFuncProf.erase(Match);
2340349cc55cSDimitry Andric       Matched = true;
2341e8d8bef9SDimitry Andric     }
2342e8d8bef9SDimitry Andric 
2343e8d8bef9SDimitry Andric     // Print function-level similarity information if specified by options.
2344e8d8bef9SDimitry Andric     assert(TestStats.count(FuncOverlap.TestName) &&
2345e8d8bef9SDimitry Andric            "TestStats should have records for all functions in test profile "
2346e8d8bef9SDimitry Andric            "except inlinees");
2347e8d8bef9SDimitry Andric     if (TestStats[FuncOverlap.TestName].MaxSample >= FuncFilter.ValueCutoff ||
2348349cc55cSDimitry Andric         (Matched && FuncOverlap.Similarity < LowSimilarityThreshold) ||
2349349cc55cSDimitry Andric         (Matched && !FuncFilter.NameFilter.empty() &&
2350349cc55cSDimitry Andric          FuncOverlap.BaseName.toString().find(FuncFilter.NameFilter) !=
2351349cc55cSDimitry Andric              std::string::npos)) {
2352e8d8bef9SDimitry Andric       assert(ProfOverlap.BaseSample > 0 &&
2353e8d8bef9SDimitry Andric              "Total samples in base profile should be greater than 0");
2354e8d8bef9SDimitry Andric       FuncOverlap.BaseWeight =
2355e8d8bef9SDimitry Andric           static_cast<double>(FuncOverlap.BaseSample) / ProfOverlap.BaseSample;
2356e8d8bef9SDimitry Andric       assert(ProfOverlap.TestSample > 0 &&
2357e8d8bef9SDimitry Andric              "Total samples in test profile should be greater than 0");
2358e8d8bef9SDimitry Andric       FuncOverlap.TestWeight =
2359e8d8bef9SDimitry Andric           static_cast<double>(FuncOverlap.TestSample) / ProfOverlap.TestSample;
2360e8d8bef9SDimitry Andric       FuncSimilarityDump.emplace(FuncOverlap.BaseWeight, FuncOverlap);
2361e8d8bef9SDimitry Andric     }
2362e8d8bef9SDimitry Andric   }
2363e8d8bef9SDimitry Andric 
2364e8d8bef9SDimitry Andric   // Traverse through functions in base profile but not in test profile.
2365e8d8bef9SDimitry Andric   for (const auto &F : BaseFuncProf) {
2366349cc55cSDimitry Andric     assert(BaseStats.count(F.second->getContext()) &&
2367e8d8bef9SDimitry Andric            "BaseStats should have records for all functions in base profile "
2368e8d8bef9SDimitry Andric            "except inlinees");
2369349cc55cSDimitry Andric     const FuncSampleStats &FuncStats = BaseStats[F.second->getContext()];
2370e8d8bef9SDimitry Andric     ++ProfOverlap.BaseUniqueCount;
2371e8d8bef9SDimitry Andric     ProfOverlap.BaseUniqueSample += FuncStats.SampleSum;
2372e8d8bef9SDimitry Andric 
2373e8d8bef9SDimitry Andric     updateHotBlockOverlap(FuncStats.SampleSum, 0, FuncStats.HotBlockCount);
2374e8d8bef9SDimitry Andric 
2375e8d8bef9SDimitry Andric     double FuncSimilarity = computeSampleFunctionOverlap(
2376e8d8bef9SDimitry Andric         nullptr, nullptr, nullptr, FuncStats.SampleSum, 0);
2377e8d8bef9SDimitry Andric     ProfOverlap.Similarity +=
2378e8d8bef9SDimitry Andric         weightByImportance(FuncSimilarity, FuncStats.SampleSum, 0);
2379e8d8bef9SDimitry Andric 
2380e8d8bef9SDimitry Andric     ProfOverlap.UnionSample += FuncStats.SampleSum;
2381e8d8bef9SDimitry Andric   }
2382e8d8bef9SDimitry Andric 
2383e8d8bef9SDimitry Andric   // Now, ProfSimilarity may be a little greater than 1 due to imprecision
2384e8d8bef9SDimitry Andric   // of floating point accumulations. Make it 1.0 if the difference is below
2385e8d8bef9SDimitry Andric   // Epsilon.
2386e8d8bef9SDimitry Andric   ProfOverlap.Similarity = (std::fabs(ProfOverlap.Similarity - 1) < Epsilon)
2387e8d8bef9SDimitry Andric                                ? 1
2388e8d8bef9SDimitry Andric                                : ProfOverlap.Similarity;
2389e8d8bef9SDimitry Andric 
2390e8d8bef9SDimitry Andric   computeHotFuncOverlap();
2391e8d8bef9SDimitry Andric }
2392e8d8bef9SDimitry Andric 
initializeSampleProfileOverlap()2393e8d8bef9SDimitry Andric void SampleOverlapAggregator::initializeSampleProfileOverlap() {
2394e8d8bef9SDimitry Andric   const auto &BaseProf = BaseReader->getProfiles();
2395e8d8bef9SDimitry Andric   for (const auto &I : BaseProf) {
2396e8d8bef9SDimitry Andric     ++ProfOverlap.BaseCount;
2397e8d8bef9SDimitry Andric     FuncSampleStats FuncStats;
2398e8d8bef9SDimitry Andric     getFuncSampleStats(I.second, FuncStats, BaseHotThreshold);
2399e8d8bef9SDimitry Andric     ProfOverlap.BaseSample += FuncStats.SampleSum;
2400349cc55cSDimitry Andric     BaseStats.emplace(I.second.getContext(), FuncStats);
2401e8d8bef9SDimitry Andric   }
2402e8d8bef9SDimitry Andric 
2403e8d8bef9SDimitry Andric   const auto &TestProf = TestReader->getProfiles();
2404e8d8bef9SDimitry Andric   for (const auto &I : TestProf) {
2405e8d8bef9SDimitry Andric     ++ProfOverlap.TestCount;
2406e8d8bef9SDimitry Andric     FuncSampleStats FuncStats;
2407e8d8bef9SDimitry Andric     getFuncSampleStats(I.second, FuncStats, TestHotThreshold);
2408e8d8bef9SDimitry Andric     ProfOverlap.TestSample += FuncStats.SampleSum;
2409349cc55cSDimitry Andric     TestStats.emplace(I.second.getContext(), FuncStats);
2410e8d8bef9SDimitry Andric   }
2411e8d8bef9SDimitry Andric 
2412e8d8bef9SDimitry Andric   ProfOverlap.BaseName = StringRef(BaseFilename);
2413e8d8bef9SDimitry Andric   ProfOverlap.TestName = StringRef(TestFilename);
2414e8d8bef9SDimitry Andric }
2415e8d8bef9SDimitry Andric 
dumpFuncSimilarity(raw_fd_ostream & OS) const2416e8d8bef9SDimitry Andric void SampleOverlapAggregator::dumpFuncSimilarity(raw_fd_ostream &OS) const {
2417e8d8bef9SDimitry Andric   using namespace sampleprof;
2418e8d8bef9SDimitry Andric 
2419e8d8bef9SDimitry Andric   if (FuncSimilarityDump.empty())
2420e8d8bef9SDimitry Andric     return;
2421e8d8bef9SDimitry Andric 
2422e8d8bef9SDimitry Andric   formatted_raw_ostream FOS(OS);
2423e8d8bef9SDimitry Andric   FOS << "Function-level details:\n";
2424e8d8bef9SDimitry Andric   FOS << "Base weight";
2425e8d8bef9SDimitry Andric   FOS.PadToColumn(TestWeightCol);
2426e8d8bef9SDimitry Andric   FOS << "Test weight";
2427e8d8bef9SDimitry Andric   FOS.PadToColumn(SimilarityCol);
2428e8d8bef9SDimitry Andric   FOS << "Similarity";
2429e8d8bef9SDimitry Andric   FOS.PadToColumn(OverlapCol);
2430e8d8bef9SDimitry Andric   FOS << "Overlap";
2431e8d8bef9SDimitry Andric   FOS.PadToColumn(BaseUniqueCol);
2432e8d8bef9SDimitry Andric   FOS << "Base unique";
2433e8d8bef9SDimitry Andric   FOS.PadToColumn(TestUniqueCol);
2434e8d8bef9SDimitry Andric   FOS << "Test unique";
2435e8d8bef9SDimitry Andric   FOS.PadToColumn(BaseSampleCol);
2436e8d8bef9SDimitry Andric   FOS << "Base samples";
2437e8d8bef9SDimitry Andric   FOS.PadToColumn(TestSampleCol);
2438e8d8bef9SDimitry Andric   FOS << "Test samples";
2439e8d8bef9SDimitry Andric   FOS.PadToColumn(FuncNameCol);
2440e8d8bef9SDimitry Andric   FOS << "Function name\n";
2441e8d8bef9SDimitry Andric   for (const auto &F : FuncSimilarityDump) {
2442e8d8bef9SDimitry Andric     double OverlapPercent =
2443e8d8bef9SDimitry Andric         F.second.UnionSample > 0
2444e8d8bef9SDimitry Andric             ? static_cast<double>(F.second.OverlapSample) / F.second.UnionSample
2445e8d8bef9SDimitry Andric             : 0;
2446e8d8bef9SDimitry Andric     double BaseUniquePercent =
2447e8d8bef9SDimitry Andric         F.second.BaseSample > 0
2448e8d8bef9SDimitry Andric             ? static_cast<double>(F.second.BaseUniqueSample) /
2449e8d8bef9SDimitry Andric                   F.second.BaseSample
2450e8d8bef9SDimitry Andric             : 0;
2451e8d8bef9SDimitry Andric     double TestUniquePercent =
2452e8d8bef9SDimitry Andric         F.second.TestSample > 0
2453e8d8bef9SDimitry Andric             ? static_cast<double>(F.second.TestUniqueSample) /
2454e8d8bef9SDimitry Andric                   F.second.TestSample
2455e8d8bef9SDimitry Andric             : 0;
2456e8d8bef9SDimitry Andric 
2457e8d8bef9SDimitry Andric     FOS << format("%.2f%%", F.second.BaseWeight * 100);
2458e8d8bef9SDimitry Andric     FOS.PadToColumn(TestWeightCol);
2459e8d8bef9SDimitry Andric     FOS << format("%.2f%%", F.second.TestWeight * 100);
2460e8d8bef9SDimitry Andric     FOS.PadToColumn(SimilarityCol);
2461e8d8bef9SDimitry Andric     FOS << format("%.2f%%", F.second.Similarity * 100);
2462e8d8bef9SDimitry Andric     FOS.PadToColumn(OverlapCol);
2463e8d8bef9SDimitry Andric     FOS << format("%.2f%%", OverlapPercent * 100);
2464e8d8bef9SDimitry Andric     FOS.PadToColumn(BaseUniqueCol);
2465e8d8bef9SDimitry Andric     FOS << format("%.2f%%", BaseUniquePercent * 100);
2466e8d8bef9SDimitry Andric     FOS.PadToColumn(TestUniqueCol);
2467e8d8bef9SDimitry Andric     FOS << format("%.2f%%", TestUniquePercent * 100);
2468e8d8bef9SDimitry Andric     FOS.PadToColumn(BaseSampleCol);
2469e8d8bef9SDimitry Andric     FOS << F.second.BaseSample;
2470e8d8bef9SDimitry Andric     FOS.PadToColumn(TestSampleCol);
2471e8d8bef9SDimitry Andric     FOS << F.second.TestSample;
2472e8d8bef9SDimitry Andric     FOS.PadToColumn(FuncNameCol);
2473349cc55cSDimitry Andric     FOS << F.second.TestName.toString() << "\n";
2474e8d8bef9SDimitry Andric   }
2475e8d8bef9SDimitry Andric }
2476e8d8bef9SDimitry Andric 
dumpProgramSummary(raw_fd_ostream & OS) const2477e8d8bef9SDimitry Andric void SampleOverlapAggregator::dumpProgramSummary(raw_fd_ostream &OS) const {
2478349cc55cSDimitry Andric   OS << "Profile overlap infomation for base_profile: "
2479349cc55cSDimitry Andric      << ProfOverlap.BaseName.toString()
2480349cc55cSDimitry Andric      << " and test_profile: " << ProfOverlap.TestName.toString()
2481349cc55cSDimitry Andric      << "\nProgram level:\n";
2482e8d8bef9SDimitry Andric 
2483e8d8bef9SDimitry Andric   OS << "  Whole program profile similarity: "
2484e8d8bef9SDimitry Andric      << format("%.3f%%", ProfOverlap.Similarity * 100) << "\n";
2485e8d8bef9SDimitry Andric 
2486e8d8bef9SDimitry Andric   assert(ProfOverlap.UnionSample > 0 &&
2487e8d8bef9SDimitry Andric          "Total samples in two profile should be greater than 0");
2488e8d8bef9SDimitry Andric   double OverlapPercent =
2489e8d8bef9SDimitry Andric       static_cast<double>(ProfOverlap.OverlapSample) / ProfOverlap.UnionSample;
2490e8d8bef9SDimitry Andric   assert(ProfOverlap.BaseSample > 0 &&
2491e8d8bef9SDimitry Andric          "Total samples in base profile should be greater than 0");
2492e8d8bef9SDimitry Andric   double BaseUniquePercent = static_cast<double>(ProfOverlap.BaseUniqueSample) /
2493e8d8bef9SDimitry Andric                              ProfOverlap.BaseSample;
2494e8d8bef9SDimitry Andric   assert(ProfOverlap.TestSample > 0 &&
2495e8d8bef9SDimitry Andric          "Total samples in test profile should be greater than 0");
2496e8d8bef9SDimitry Andric   double TestUniquePercent = static_cast<double>(ProfOverlap.TestUniqueSample) /
2497e8d8bef9SDimitry Andric                              ProfOverlap.TestSample;
2498e8d8bef9SDimitry Andric 
2499e8d8bef9SDimitry Andric   OS << "  Whole program sample overlap: "
2500e8d8bef9SDimitry Andric      << format("%.3f%%", OverlapPercent * 100) << "\n";
2501e8d8bef9SDimitry Andric   OS << "    percentage of samples unique in base profile: "
2502e8d8bef9SDimitry Andric      << format("%.3f%%", BaseUniquePercent * 100) << "\n";
2503e8d8bef9SDimitry Andric   OS << "    percentage of samples unique in test profile: "
2504e8d8bef9SDimitry Andric      << format("%.3f%%", TestUniquePercent * 100) << "\n";
2505e8d8bef9SDimitry Andric   OS << "    total samples in base profile: " << ProfOverlap.BaseSample << "\n"
2506e8d8bef9SDimitry Andric      << "    total samples in test profile: " << ProfOverlap.TestSample << "\n";
2507e8d8bef9SDimitry Andric 
2508e8d8bef9SDimitry Andric   assert(ProfOverlap.UnionCount > 0 &&
2509e8d8bef9SDimitry Andric          "There should be at least one function in two input profiles");
2510e8d8bef9SDimitry Andric   double FuncOverlapPercent =
2511e8d8bef9SDimitry Andric       static_cast<double>(ProfOverlap.OverlapCount) / ProfOverlap.UnionCount;
2512e8d8bef9SDimitry Andric   OS << "  Function overlap: " << format("%.3f%%", FuncOverlapPercent * 100)
2513e8d8bef9SDimitry Andric      << "\n";
2514e8d8bef9SDimitry Andric   OS << "    overlap functions: " << ProfOverlap.OverlapCount << "\n";
2515e8d8bef9SDimitry Andric   OS << "    functions unique in base profile: " << ProfOverlap.BaseUniqueCount
2516e8d8bef9SDimitry Andric      << "\n";
2517e8d8bef9SDimitry Andric   OS << "    functions unique in test profile: " << ProfOverlap.TestUniqueCount
2518e8d8bef9SDimitry Andric      << "\n";
2519e8d8bef9SDimitry Andric }
2520e8d8bef9SDimitry Andric 
dumpHotFuncAndBlockOverlap(raw_fd_ostream & OS) const2521e8d8bef9SDimitry Andric void SampleOverlapAggregator::dumpHotFuncAndBlockOverlap(
2522e8d8bef9SDimitry Andric     raw_fd_ostream &OS) const {
2523e8d8bef9SDimitry Andric   assert(HotFuncOverlap.UnionCount > 0 &&
2524e8d8bef9SDimitry Andric          "There should be at least one hot function in two input profiles");
2525e8d8bef9SDimitry Andric   OS << "  Hot-function overlap: "
2526e8d8bef9SDimitry Andric      << format("%.3f%%", static_cast<double>(HotFuncOverlap.OverlapCount) /
2527e8d8bef9SDimitry Andric                              HotFuncOverlap.UnionCount * 100)
2528e8d8bef9SDimitry Andric      << "\n";
2529e8d8bef9SDimitry Andric   OS << "    overlap hot functions: " << HotFuncOverlap.OverlapCount << "\n";
2530e8d8bef9SDimitry Andric   OS << "    hot functions unique in base profile: "
2531e8d8bef9SDimitry Andric      << HotFuncOverlap.BaseCount - HotFuncOverlap.OverlapCount << "\n";
2532e8d8bef9SDimitry Andric   OS << "    hot functions unique in test profile: "
2533e8d8bef9SDimitry Andric      << HotFuncOverlap.TestCount - HotFuncOverlap.OverlapCount << "\n";
2534e8d8bef9SDimitry Andric 
2535e8d8bef9SDimitry Andric   assert(HotBlockOverlap.UnionCount > 0 &&
2536e8d8bef9SDimitry Andric          "There should be at least one hot block in two input profiles");
2537e8d8bef9SDimitry Andric   OS << "  Hot-block overlap: "
2538e8d8bef9SDimitry Andric      << format("%.3f%%", static_cast<double>(HotBlockOverlap.OverlapCount) /
2539e8d8bef9SDimitry Andric                              HotBlockOverlap.UnionCount * 100)
2540e8d8bef9SDimitry Andric      << "\n";
2541e8d8bef9SDimitry Andric   OS << "    overlap hot blocks: " << HotBlockOverlap.OverlapCount << "\n";
2542e8d8bef9SDimitry Andric   OS << "    hot blocks unique in base profile: "
2543e8d8bef9SDimitry Andric      << HotBlockOverlap.BaseCount - HotBlockOverlap.OverlapCount << "\n";
2544e8d8bef9SDimitry Andric   OS << "    hot blocks unique in test profile: "
2545e8d8bef9SDimitry Andric      << HotBlockOverlap.TestCount - HotBlockOverlap.OverlapCount << "\n";
2546e8d8bef9SDimitry Andric }
2547e8d8bef9SDimitry Andric 
loadProfiles()2548e8d8bef9SDimitry Andric std::error_code SampleOverlapAggregator::loadProfiles() {
2549e8d8bef9SDimitry Andric   using namespace sampleprof;
2550e8d8bef9SDimitry Andric 
2551e8d8bef9SDimitry Andric   LLVMContext Context;
255206c3fb27SDimitry Andric   auto FS = vfs::getRealFileSystem();
255306c3fb27SDimitry Andric   auto BaseReaderOrErr = SampleProfileReader::create(BaseFilename, Context, *FS,
2554fe6060f1SDimitry Andric                                                      FSDiscriminatorPassOption);
2555e8d8bef9SDimitry Andric   if (std::error_code EC = BaseReaderOrErr.getError())
2556e8d8bef9SDimitry Andric     exitWithErrorCode(EC, BaseFilename);
2557e8d8bef9SDimitry Andric 
255806c3fb27SDimitry Andric   auto TestReaderOrErr = SampleProfileReader::create(TestFilename, Context, *FS,
2559fe6060f1SDimitry Andric                                                      FSDiscriminatorPassOption);
2560e8d8bef9SDimitry Andric   if (std::error_code EC = TestReaderOrErr.getError())
2561e8d8bef9SDimitry Andric     exitWithErrorCode(EC, TestFilename);
2562e8d8bef9SDimitry Andric 
2563e8d8bef9SDimitry Andric   BaseReader = std::move(BaseReaderOrErr.get());
2564e8d8bef9SDimitry Andric   TestReader = std::move(TestReaderOrErr.get());
2565e8d8bef9SDimitry Andric 
2566e8d8bef9SDimitry Andric   if (std::error_code EC = BaseReader->read())
2567e8d8bef9SDimitry Andric     exitWithErrorCode(EC, BaseFilename);
2568e8d8bef9SDimitry Andric   if (std::error_code EC = TestReader->read())
2569e8d8bef9SDimitry Andric     exitWithErrorCode(EC, TestFilename);
2570e8d8bef9SDimitry Andric   if (BaseReader->profileIsProbeBased() != TestReader->profileIsProbeBased())
2571e8d8bef9SDimitry Andric     exitWithError(
2572e8d8bef9SDimitry Andric         "cannot compare probe-based profile with non-probe-based profile");
257381ad6265SDimitry Andric   if (BaseReader->profileIsCS() != TestReader->profileIsCS())
2574fe6060f1SDimitry Andric     exitWithError("cannot compare CS profile with non-CS profile");
2575e8d8bef9SDimitry Andric 
2576e8d8bef9SDimitry Andric   // Load BaseHotThreshold and TestHotThreshold as 99-percentile threshold in
2577e8d8bef9SDimitry Andric   // profile summary.
2578e8d8bef9SDimitry Andric   ProfileSummary &BasePS = BaseReader->getSummary();
2579e8d8bef9SDimitry Andric   ProfileSummary &TestPS = TestReader->getSummary();
2580349cc55cSDimitry Andric   BaseHotThreshold =
2581349cc55cSDimitry Andric       ProfileSummaryBuilder::getHotCountThreshold(BasePS.getDetailedSummary());
2582349cc55cSDimitry Andric   TestHotThreshold =
2583349cc55cSDimitry Andric       ProfileSummaryBuilder::getHotCountThreshold(TestPS.getDetailedSummary());
2584349cc55cSDimitry Andric 
2585e8d8bef9SDimitry Andric   return std::error_code();
2586e8d8bef9SDimitry Andric }
2587e8d8bef9SDimitry Andric 
overlapSampleProfile(const std::string & BaseFilename,const std::string & TestFilename,const OverlapFuncFilters & FuncFilter,uint64_t SimilarityCutoff,raw_fd_ostream & OS)2588e8d8bef9SDimitry Andric void overlapSampleProfile(const std::string &BaseFilename,
2589e8d8bef9SDimitry Andric                           const std::string &TestFilename,
2590e8d8bef9SDimitry Andric                           const OverlapFuncFilters &FuncFilter,
2591e8d8bef9SDimitry Andric                           uint64_t SimilarityCutoff, raw_fd_ostream &OS) {
2592e8d8bef9SDimitry Andric   using namespace sampleprof;
2593e8d8bef9SDimitry Andric 
2594e8d8bef9SDimitry Andric   // We use 0.000005 to initialize OverlapAggr.Epsilon because the final metrics
2595e8d8bef9SDimitry Andric   // report 2--3 places after decimal point in percentage numbers.
2596e8d8bef9SDimitry Andric   SampleOverlapAggregator OverlapAggr(
2597e8d8bef9SDimitry Andric       BaseFilename, TestFilename,
2598e8d8bef9SDimitry Andric       static_cast<double>(SimilarityCutoff) / 1000000, 0.000005, FuncFilter);
2599e8d8bef9SDimitry Andric   if (std::error_code EC = OverlapAggr.loadProfiles())
2600e8d8bef9SDimitry Andric     exitWithErrorCode(EC);
2601e8d8bef9SDimitry Andric 
2602e8d8bef9SDimitry Andric   OverlapAggr.initializeSampleProfileOverlap();
2603e8d8bef9SDimitry Andric   if (OverlapAggr.detectZeroSampleProfile(OS))
2604e8d8bef9SDimitry Andric     return;
2605e8d8bef9SDimitry Andric 
2606e8d8bef9SDimitry Andric   OverlapAggr.computeSampleProfileOverlap(OS);
2607e8d8bef9SDimitry Andric 
2608e8d8bef9SDimitry Andric   OverlapAggr.dumpProgramSummary(OS);
2609e8d8bef9SDimitry Andric   OverlapAggr.dumpHotFuncAndBlockOverlap(OS);
2610e8d8bef9SDimitry Andric   OverlapAggr.dumpFuncSimilarity(OS);
2611e8d8bef9SDimitry Andric }
2612e8d8bef9SDimitry Andric 
overlap_main(int argc,const char * argv[])26130b57cec5SDimitry Andric static int overlap_main(int argc, const char *argv[]) {
26140b57cec5SDimitry Andric   std::error_code EC;
26155f757f3fSDimitry Andric   raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);
26160b57cec5SDimitry Andric   if (EC)
26175f757f3fSDimitry Andric     exitWithErrorCode(EC, OutputFilename);
26180b57cec5SDimitry Andric 
2619e8d8bef9SDimitry Andric   if (ProfileKind == instr)
26200b57cec5SDimitry Andric     overlapInstrProfile(BaseFilename, TestFilename,
26215f757f3fSDimitry Andric                         OverlapFuncFilters{OverlapValueCutoff, FuncNameFilter},
26225f757f3fSDimitry Andric                         OS, IsCS);
2623e8d8bef9SDimitry Andric   else
2624e8d8bef9SDimitry Andric     overlapSampleProfile(BaseFilename, TestFilename,
26255f757f3fSDimitry Andric                          OverlapFuncFilters{OverlapValueCutoff, FuncNameFilter},
2626e8d8bef9SDimitry Andric                          SimilarityCutoff, OS);
26270b57cec5SDimitry Andric 
26280b57cec5SDimitry Andric   return 0;
26290b57cec5SDimitry Andric }
26300b57cec5SDimitry Andric 
2631fe6060f1SDimitry Andric namespace {
2632fe6060f1SDimitry Andric struct ValueSitesStats {
2633cb14a3feSDimitry Andric   ValueSitesStats() = default;
2634cb14a3feSDimitry Andric   uint64_t TotalNumValueSites = 0;
2635cb14a3feSDimitry Andric   uint64_t TotalNumValueSitesWithValueProfile = 0;
2636cb14a3feSDimitry Andric   uint64_t TotalNumValues = 0;
26370b57cec5SDimitry Andric   std::vector<unsigned> ValueSitesHistogram;
2638fe6060f1SDimitry Andric };
2639fe6060f1SDimitry Andric } // namespace
26400b57cec5SDimitry Andric 
traverseAllValueSites(const InstrProfRecord & Func,uint32_t VK,ValueSitesStats & Stats,raw_fd_ostream & OS,InstrProfSymtab * Symtab)26410b57cec5SDimitry Andric static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK,
26420b57cec5SDimitry Andric                                   ValueSitesStats &Stats, raw_fd_ostream &OS,
26430b57cec5SDimitry Andric                                   InstrProfSymtab *Symtab) {
26440b57cec5SDimitry Andric   uint32_t NS = Func.getNumValueSites(VK);
26450b57cec5SDimitry Andric   Stats.TotalNumValueSites += NS;
26460b57cec5SDimitry Andric   for (size_t I = 0; I < NS; ++I) {
26470b57cec5SDimitry Andric     uint32_t NV = Func.getNumValueDataForSite(VK, I);
26480b57cec5SDimitry Andric     std::unique_ptr<InstrProfValueData[]> VD = Func.getValueForSite(VK, I);
26490b57cec5SDimitry Andric     Stats.TotalNumValues += NV;
26500b57cec5SDimitry Andric     if (NV) {
26510b57cec5SDimitry Andric       Stats.TotalNumValueSitesWithValueProfile++;
26520b57cec5SDimitry Andric       if (NV > Stats.ValueSitesHistogram.size())
26530b57cec5SDimitry Andric         Stats.ValueSitesHistogram.resize(NV, 0);
26540b57cec5SDimitry Andric       Stats.ValueSitesHistogram[NV - 1]++;
26550b57cec5SDimitry Andric     }
26560b57cec5SDimitry Andric 
26570b57cec5SDimitry Andric     uint64_t SiteSum = 0;
26580b57cec5SDimitry Andric     for (uint32_t V = 0; V < NV; V++)
26590b57cec5SDimitry Andric       SiteSum += VD[V].Count;
26600b57cec5SDimitry Andric     if (SiteSum == 0)
26610b57cec5SDimitry Andric       SiteSum = 1;
26620b57cec5SDimitry Andric 
26630b57cec5SDimitry Andric     for (uint32_t V = 0; V < NV; V++) {
26640b57cec5SDimitry Andric       OS << "\t[ " << format("%2u", I) << ", ";
26650b57cec5SDimitry Andric       if (Symtab == nullptr)
26660b57cec5SDimitry Andric         OS << format("%4" PRIu64, VD[V].Value);
26670b57cec5SDimitry Andric       else
26685f757f3fSDimitry Andric         OS << Symtab->getFuncOrVarName(VD[V].Value);
26690b57cec5SDimitry Andric       OS << ", " << format("%10" PRId64, VD[V].Count) << " ] ("
26700b57cec5SDimitry Andric          << format("%.2f%%", (VD[V].Count * 100.0 / SiteSum)) << ")\n";
26710b57cec5SDimitry Andric     }
26720b57cec5SDimitry Andric   }
26730b57cec5SDimitry Andric }
26740b57cec5SDimitry Andric 
showValueSitesStats(raw_fd_ostream & OS,uint32_t VK,ValueSitesStats & Stats)26750b57cec5SDimitry Andric static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK,
26760b57cec5SDimitry Andric                                 ValueSitesStats &Stats) {
26770b57cec5SDimitry Andric   OS << "  Total number of sites: " << Stats.TotalNumValueSites << "\n";
26780b57cec5SDimitry Andric   OS << "  Total number of sites with values: "
26790b57cec5SDimitry Andric      << Stats.TotalNumValueSitesWithValueProfile << "\n";
26800b57cec5SDimitry Andric   OS << "  Total number of profiled values: " << Stats.TotalNumValues << "\n";
26810b57cec5SDimitry Andric 
26820b57cec5SDimitry Andric   OS << "  Value sites histogram:\n\tNumTargets, SiteCount\n";
26830b57cec5SDimitry Andric   for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) {
26840b57cec5SDimitry Andric     if (Stats.ValueSitesHistogram[I] > 0)
26850b57cec5SDimitry Andric       OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n";
26860b57cec5SDimitry Andric   }
26870b57cec5SDimitry Andric }
26880b57cec5SDimitry Andric 
showInstrProfile(ShowFormat SFormat,raw_fd_ostream & OS)26895f757f3fSDimitry Andric static int showInstrProfile(ShowFormat SFormat, raw_fd_ostream &OS) {
2690bdd1243dSDimitry Andric   if (SFormat == ShowFormat::Json)
2691bdd1243dSDimitry Andric     exitWithError("JSON output is not supported for instr profiles");
2692bdd1243dSDimitry Andric   if (SFormat == ShowFormat::Yaml)
2693bdd1243dSDimitry Andric     exitWithError("YAML output is not supported for instr profiles");
269406c3fb27SDimitry Andric   auto FS = vfs::getRealFileSystem();
269506c3fb27SDimitry Andric   auto ReaderOrErr = InstrProfReader::create(Filename, *FS);
26960b57cec5SDimitry Andric   std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs);
26970b57cec5SDimitry Andric   if (ShowDetailedSummary && Cutoffs.empty()) {
269881ad6265SDimitry Andric     Cutoffs = ProfileSummaryBuilder::DefaultCutoffs;
26990b57cec5SDimitry Andric   }
27000b57cec5SDimitry Andric   InstrProfSummaryBuilder Builder(std::move(Cutoffs));
27010b57cec5SDimitry Andric   if (Error E = ReaderOrErr.takeError())
27020b57cec5SDimitry Andric     exitWithError(std::move(E), Filename);
27030b57cec5SDimitry Andric 
27040b57cec5SDimitry Andric   auto Reader = std::move(ReaderOrErr.get());
27050b57cec5SDimitry Andric   bool IsIRInstr = Reader->isIRLevelProfile();
27060b57cec5SDimitry Andric   size_t ShownFunctions = 0;
27070b57cec5SDimitry Andric   size_t BelowCutoffFunctions = 0;
27080b57cec5SDimitry Andric   int NumVPKind = IPVK_Last - IPVK_First + 1;
27090b57cec5SDimitry Andric   std::vector<ValueSitesStats> VPStats(NumVPKind);
27100b57cec5SDimitry Andric 
27110b57cec5SDimitry Andric   auto MinCmp = [](const std::pair<std::string, uint64_t> &v1,
27120b57cec5SDimitry Andric                    const std::pair<std::string, uint64_t> &v2) {
27130b57cec5SDimitry Andric     return v1.second > v2.second;
27140b57cec5SDimitry Andric   };
27150b57cec5SDimitry Andric 
27160b57cec5SDimitry Andric   std::priority_queue<std::pair<std::string, uint64_t>,
27170b57cec5SDimitry Andric                       std::vector<std::pair<std::string, uint64_t>>,
27180b57cec5SDimitry Andric                       decltype(MinCmp)>
27190b57cec5SDimitry Andric       HottestFuncs(MinCmp);
27200b57cec5SDimitry Andric 
27210b57cec5SDimitry Andric   if (!TextFormat && OnlyListBelow) {
27220b57cec5SDimitry Andric     OS << "The list of functions with the maximum counter less than "
27235f757f3fSDimitry Andric        << ShowValueCutoff << ":\n";
27240b57cec5SDimitry Andric   }
27250b57cec5SDimitry Andric 
27260b57cec5SDimitry Andric   // Add marker so that IR-level instrumentation round-trips properly.
27270b57cec5SDimitry Andric   if (TextFormat && IsIRInstr)
27280b57cec5SDimitry Andric     OS << ":ir\n";
27290b57cec5SDimitry Andric 
27300b57cec5SDimitry Andric   for (const auto &Func : *Reader) {
27310b57cec5SDimitry Andric     if (Reader->isIRLevelProfile()) {
27320b57cec5SDimitry Andric       bool FuncIsCS = NamedInstrProfRecord::hasCSFlagInHash(Func.Hash);
27330b57cec5SDimitry Andric       if (FuncIsCS != ShowCS)
27340b57cec5SDimitry Andric         continue;
27350b57cec5SDimitry Andric     }
2736349cc55cSDimitry Andric     bool Show = ShowAllFunctions ||
27375f757f3fSDimitry Andric                 (!FuncNameFilter.empty() && Func.Name.contains(FuncNameFilter));
27380b57cec5SDimitry Andric 
27390b57cec5SDimitry Andric     bool doTextFormatDump = (Show && TextFormat);
27400b57cec5SDimitry Andric 
27410b57cec5SDimitry Andric     if (doTextFormatDump) {
27420b57cec5SDimitry Andric       InstrProfSymtab &Symtab = Reader->getSymtab();
27430b57cec5SDimitry Andric       InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab,
27440b57cec5SDimitry Andric                                          OS);
27450b57cec5SDimitry Andric       continue;
27460b57cec5SDimitry Andric     }
27470b57cec5SDimitry Andric 
27480b57cec5SDimitry Andric     assert(Func.Counts.size() > 0 && "function missing entry counter");
27490b57cec5SDimitry Andric     Builder.addRecord(Func);
27500b57cec5SDimitry Andric 
27511fd87a68SDimitry Andric     if (ShowCovered) {
2752972a253aSDimitry Andric       if (llvm::any_of(Func.Counts, [](uint64_t C) { return C; }))
27531fd87a68SDimitry Andric         OS << Func.Name << "\n";
27541fd87a68SDimitry Andric       continue;
27551fd87a68SDimitry Andric     }
27561fd87a68SDimitry Andric 
27570b57cec5SDimitry Andric     uint64_t FuncMax = 0;
27580b57cec5SDimitry Andric     uint64_t FuncSum = 0;
2759bdd1243dSDimitry Andric 
2760bdd1243dSDimitry Andric     auto PseudoKind = Func.getCountPseudoKind();
2761bdd1243dSDimitry Andric     if (PseudoKind != InstrProfRecord::NotPseudo) {
2762bdd1243dSDimitry Andric       if (Show) {
2763bdd1243dSDimitry Andric         if (!ShownFunctions)
2764bdd1243dSDimitry Andric           OS << "Counters:\n";
2765bdd1243dSDimitry Andric         ++ShownFunctions;
2766bdd1243dSDimitry Andric         OS << "  " << Func.Name << ":\n"
2767bdd1243dSDimitry Andric            << "    Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n"
2768bdd1243dSDimitry Andric            << "    Counters: " << Func.Counts.size();
2769bdd1243dSDimitry Andric         if (PseudoKind == InstrProfRecord::PseudoHot)
2770bdd1243dSDimitry Andric           OS << "    <PseudoHot>\n";
2771bdd1243dSDimitry Andric         else if (PseudoKind == InstrProfRecord::PseudoWarm)
2772bdd1243dSDimitry Andric           OS << "    <PseudoWarm>\n";
2773bdd1243dSDimitry Andric         else
2774bdd1243dSDimitry Andric           llvm_unreachable("Unknown PseudoKind");
2775bdd1243dSDimitry Andric       }
2776e8d8bef9SDimitry Andric       continue;
2777bdd1243dSDimitry Andric     }
2778bdd1243dSDimitry Andric 
2779bdd1243dSDimitry Andric     for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) {
27800b57cec5SDimitry Andric       FuncMax = std::max(FuncMax, Func.Counts[I]);
27810b57cec5SDimitry Andric       FuncSum += Func.Counts[I];
27820b57cec5SDimitry Andric     }
27830b57cec5SDimitry Andric 
27845f757f3fSDimitry Andric     if (FuncMax < ShowValueCutoff) {
27850b57cec5SDimitry Andric       ++BelowCutoffFunctions;
27860b57cec5SDimitry Andric       if (OnlyListBelow) {
27870b57cec5SDimitry Andric         OS << "  " << Func.Name << ": (Max = " << FuncMax
27880b57cec5SDimitry Andric            << " Sum = " << FuncSum << ")\n";
27890b57cec5SDimitry Andric       }
27900b57cec5SDimitry Andric       continue;
27910b57cec5SDimitry Andric     } else if (OnlyListBelow)
27920b57cec5SDimitry Andric       continue;
27930b57cec5SDimitry Andric 
27945f757f3fSDimitry Andric     if (TopNFunctions) {
27955f757f3fSDimitry Andric       if (HottestFuncs.size() == TopNFunctions) {
27960b57cec5SDimitry Andric         if (HottestFuncs.top().second < FuncMax) {
27970b57cec5SDimitry Andric           HottestFuncs.pop();
27980b57cec5SDimitry Andric           HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
27990b57cec5SDimitry Andric         }
28000b57cec5SDimitry Andric       } else
28010b57cec5SDimitry Andric         HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
28020b57cec5SDimitry Andric     }
28030b57cec5SDimitry Andric 
28040b57cec5SDimitry Andric     if (Show) {
28050b57cec5SDimitry Andric       if (!ShownFunctions)
28060b57cec5SDimitry Andric         OS << "Counters:\n";
28070b57cec5SDimitry Andric 
28080b57cec5SDimitry Andric       ++ShownFunctions;
28090b57cec5SDimitry Andric 
28100b57cec5SDimitry Andric       OS << "  " << Func.Name << ":\n"
28110b57cec5SDimitry Andric          << "    Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n"
28120b57cec5SDimitry Andric          << "    Counters: " << Func.Counts.size() << "\n";
28130b57cec5SDimitry Andric       if (!IsIRInstr)
28140b57cec5SDimitry Andric         OS << "    Function count: " << Func.Counts[0] << "\n";
28150b57cec5SDimitry Andric 
28160b57cec5SDimitry Andric       if (ShowIndirectCallTargets)
28170b57cec5SDimitry Andric         OS << "    Indirect Call Site Count: "
28180b57cec5SDimitry Andric            << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n";
28190b57cec5SDimitry Andric 
28200b57cec5SDimitry Andric       uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize);
28210b57cec5SDimitry Andric       if (ShowMemOPSizes && NumMemOPCalls > 0)
28220b57cec5SDimitry Andric         OS << "    Number of Memory Intrinsics Calls: " << NumMemOPCalls
28230b57cec5SDimitry Andric            << "\n";
28240b57cec5SDimitry Andric 
28250b57cec5SDimitry Andric       if (ShowCounts) {
28260b57cec5SDimitry Andric         OS << "    Block counts: [";
28270b57cec5SDimitry Andric         size_t Start = (IsIRInstr ? 0 : 1);
28280b57cec5SDimitry Andric         for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) {
28290b57cec5SDimitry Andric           OS << (I == Start ? "" : ", ") << Func.Counts[I];
28300b57cec5SDimitry Andric         }
28310b57cec5SDimitry Andric         OS << "]\n";
28320b57cec5SDimitry Andric       }
28330b57cec5SDimitry Andric 
28340b57cec5SDimitry Andric       if (ShowIndirectCallTargets) {
28350b57cec5SDimitry Andric         OS << "    Indirect Target Results:\n";
28360b57cec5SDimitry Andric         traverseAllValueSites(Func, IPVK_IndirectCallTarget,
28370b57cec5SDimitry Andric                               VPStats[IPVK_IndirectCallTarget], OS,
28380b57cec5SDimitry Andric                               &(Reader->getSymtab()));
28390b57cec5SDimitry Andric       }
28400b57cec5SDimitry Andric 
28410b57cec5SDimitry Andric       if (ShowMemOPSizes && NumMemOPCalls > 0) {
28420b57cec5SDimitry Andric         OS << "    Memory Intrinsic Size Results:\n";
28430b57cec5SDimitry Andric         traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS,
28440b57cec5SDimitry Andric                               nullptr);
28450b57cec5SDimitry Andric       }
28460b57cec5SDimitry Andric     }
28470b57cec5SDimitry Andric   }
28480b57cec5SDimitry Andric   if (Reader->hasError())
28490b57cec5SDimitry Andric     exitWithError(Reader->getError(), Filename);
28500b57cec5SDimitry Andric 
28511fd87a68SDimitry Andric   if (TextFormat || ShowCovered)
28520b57cec5SDimitry Andric     return 0;
28530b57cec5SDimitry Andric   std::unique_ptr<ProfileSummary> PS(Builder.getSummary());
2854e8d8bef9SDimitry Andric   bool IsIR = Reader->isIRLevelProfile();
2855e8d8bef9SDimitry Andric   OS << "Instrumentation level: " << (IsIR ? "IR" : "Front-end");
2856e8d8bef9SDimitry Andric   if (IsIR)
2857e8d8bef9SDimitry Andric     OS << "  entry_first = " << Reader->instrEntryBBEnabled();
2858e8d8bef9SDimitry Andric   OS << "\n";
28595f757f3fSDimitry Andric   if (ShowAllFunctions || !FuncNameFilter.empty())
28600b57cec5SDimitry Andric     OS << "Functions shown: " << ShownFunctions << "\n";
28610b57cec5SDimitry Andric   OS << "Total functions: " << PS->getNumFunctions() << "\n";
28625f757f3fSDimitry Andric   if (ShowValueCutoff > 0) {
28635f757f3fSDimitry Andric     OS << "Number of functions with maximum count (< " << ShowValueCutoff
28640b57cec5SDimitry Andric        << "): " << BelowCutoffFunctions << "\n";
28655f757f3fSDimitry Andric     OS << "Number of functions with maximum count (>= " << ShowValueCutoff
28660b57cec5SDimitry Andric        << "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n";
28670b57cec5SDimitry Andric   }
28680b57cec5SDimitry Andric   OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n";
28690b57cec5SDimitry Andric   OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n";
28700b57cec5SDimitry Andric 
28715f757f3fSDimitry Andric   if (TopNFunctions) {
28720b57cec5SDimitry Andric     std::vector<std::pair<std::string, uint64_t>> SortedHottestFuncs;
28730b57cec5SDimitry Andric     while (!HottestFuncs.empty()) {
28740b57cec5SDimitry Andric       SortedHottestFuncs.emplace_back(HottestFuncs.top());
28750b57cec5SDimitry Andric       HottestFuncs.pop();
28760b57cec5SDimitry Andric     }
28775f757f3fSDimitry Andric     OS << "Top " << TopNFunctions
28780b57cec5SDimitry Andric        << " functions with the largest internal block counts: \n";
28790b57cec5SDimitry Andric     for (auto &hotfunc : llvm::reverse(SortedHottestFuncs))
28800b57cec5SDimitry Andric       OS << "  " << hotfunc.first << ", max count = " << hotfunc.second << "\n";
28810b57cec5SDimitry Andric   }
28820b57cec5SDimitry Andric 
28830b57cec5SDimitry Andric   if (ShownFunctions && ShowIndirectCallTargets) {
28840b57cec5SDimitry Andric     OS << "Statistics for indirect call sites profile:\n";
28850b57cec5SDimitry Andric     showValueSitesStats(OS, IPVK_IndirectCallTarget,
28860b57cec5SDimitry Andric                         VPStats[IPVK_IndirectCallTarget]);
28870b57cec5SDimitry Andric   }
28880b57cec5SDimitry Andric 
28890b57cec5SDimitry Andric   if (ShownFunctions && ShowMemOPSizes) {
28900b57cec5SDimitry Andric     OS << "Statistics for memory intrinsic calls sizes profile:\n";
28910b57cec5SDimitry Andric     showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]);
28920b57cec5SDimitry Andric   }
28930b57cec5SDimitry Andric 
28940b57cec5SDimitry Andric   if (ShowDetailedSummary) {
28950b57cec5SDimitry Andric     OS << "Total number of blocks: " << PS->getNumCounts() << "\n";
28960b57cec5SDimitry Andric     OS << "Total count: " << PS->getTotalCount() << "\n";
28975ffd83dbSDimitry Andric     PS->printDetailedSummary(OS);
28980b57cec5SDimitry Andric   }
2899fe6060f1SDimitry Andric 
2900fe6060f1SDimitry Andric   if (ShowBinaryIds)
2901fe6060f1SDimitry Andric     if (Error E = Reader->printBinaryIds(OS))
2902fe6060f1SDimitry Andric       exitWithError(std::move(E), Filename);
2903fe6060f1SDimitry Andric 
2904bdd1243dSDimitry Andric   if (ShowProfileVersion)
2905bdd1243dSDimitry Andric     OS << "Profile version: " << Reader->getVersion() << "\n";
290606c3fb27SDimitry Andric 
290706c3fb27SDimitry Andric   if (ShowTemporalProfTraces) {
290806c3fb27SDimitry Andric     auto &Traces = Reader->getTemporalProfTraces();
290906c3fb27SDimitry Andric     OS << "Temporal Profile Traces (samples=" << Traces.size()
291006c3fb27SDimitry Andric        << " seen=" << Reader->getTemporalProfTraceStreamSize() << "):\n";
291106c3fb27SDimitry Andric     for (unsigned i = 0; i < Traces.size(); i++) {
291206c3fb27SDimitry Andric       OS << "  Temporal Profile Trace " << i << " (weight=" << Traces[i].Weight
291306c3fb27SDimitry Andric          << " count=" << Traces[i].FunctionNameRefs.size() << "):\n";
291406c3fb27SDimitry Andric       for (auto &NameRef : Traces[i].FunctionNameRefs)
29155f757f3fSDimitry Andric         OS << "    " << Reader->getSymtab().getFuncOrVarName(NameRef) << "\n";
291606c3fb27SDimitry Andric     }
291706c3fb27SDimitry Andric   }
291806c3fb27SDimitry Andric 
29190b57cec5SDimitry Andric   return 0;
29200b57cec5SDimitry Andric }
29210b57cec5SDimitry Andric 
showSectionInfo(sampleprof::SampleProfileReader * Reader,raw_fd_ostream & OS)29228bcb0991SDimitry Andric static void showSectionInfo(sampleprof::SampleProfileReader *Reader,
29238bcb0991SDimitry Andric                             raw_fd_ostream &OS) {
29248bcb0991SDimitry Andric   if (!Reader->dumpSectionInfo(OS)) {
29258bcb0991SDimitry Andric     WithColor::warning() << "-show-sec-info-only is only supported for "
29268bcb0991SDimitry Andric                          << "sample profile in extbinary format and is "
29278bcb0991SDimitry Andric                          << "ignored for other formats.\n";
29288bcb0991SDimitry Andric     return;
29298bcb0991SDimitry Andric   }
29308bcb0991SDimitry Andric }
29318bcb0991SDimitry Andric 
29325ffd83dbSDimitry Andric namespace {
29335ffd83dbSDimitry Andric struct HotFuncInfo {
2934349cc55cSDimitry Andric   std::string FuncName;
2935cb14a3feSDimitry Andric   uint64_t TotalCount = 0;
2936cb14a3feSDimitry Andric   double TotalCountPercent = 0.0f;
2937cb14a3feSDimitry Andric   uint64_t MaxCount = 0;
2938cb14a3feSDimitry Andric   uint64_t EntryCount = 0;
29395ffd83dbSDimitry Andric 
2940cb14a3feSDimitry Andric   HotFuncInfo() = default;
29415ffd83dbSDimitry Andric 
HotFuncInfo__anonae06d1131611::HotFuncInfo29425ffd83dbSDimitry Andric   HotFuncInfo(StringRef FN, uint64_t TS, double TSP, uint64_t MS, uint64_t ES)
2943349cc55cSDimitry Andric       : FuncName(FN.begin(), FN.end()), TotalCount(TS), TotalCountPercent(TSP),
2944349cc55cSDimitry Andric         MaxCount(MS), EntryCount(ES) {}
29455ffd83dbSDimitry Andric };
29465ffd83dbSDimitry Andric } // namespace
29475ffd83dbSDimitry Andric 
29485ffd83dbSDimitry Andric // Print out detailed information about hot functions in PrintValues vector.
29495ffd83dbSDimitry Andric // Users specify titles and offset of every columns through ColumnTitle and
29505ffd83dbSDimitry Andric // ColumnOffset. The size of ColumnTitle and ColumnOffset need to be the same
29515ffd83dbSDimitry Andric // and at least 4. Besides, users can optionally give a HotFuncMetric string to
29525ffd83dbSDimitry Andric // 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)29535ffd83dbSDimitry Andric static void dumpHotFunctionList(const std::vector<std::string> &ColumnTitle,
29545ffd83dbSDimitry Andric                                 const std::vector<int> &ColumnOffset,
29555ffd83dbSDimitry Andric                                 const std::vector<HotFuncInfo> &PrintValues,
29565ffd83dbSDimitry Andric                                 uint64_t HotFuncCount, uint64_t TotalFuncCount,
29575ffd83dbSDimitry Andric                                 uint64_t HotProfCount, uint64_t TotalProfCount,
29585ffd83dbSDimitry Andric                                 const std::string &HotFuncMetric,
2959349cc55cSDimitry Andric                                 uint32_t TopNFunctions, raw_fd_ostream &OS) {
2960e8d8bef9SDimitry Andric   assert(ColumnOffset.size() == ColumnTitle.size() &&
2961e8d8bef9SDimitry Andric          "ColumnOffset and ColumnTitle should have the same size");
2962e8d8bef9SDimitry Andric   assert(ColumnTitle.size() >= 4 &&
2963e8d8bef9SDimitry Andric          "ColumnTitle should have at least 4 elements");
2964e8d8bef9SDimitry Andric   assert(TotalFuncCount > 0 &&
2965e8d8bef9SDimitry Andric          "There should be at least one function in the profile");
29665ffd83dbSDimitry Andric   double TotalProfPercent = 0;
29675ffd83dbSDimitry Andric   if (TotalProfCount > 0)
2968e8d8bef9SDimitry Andric     TotalProfPercent = static_cast<double>(HotProfCount) / TotalProfCount * 100;
29695ffd83dbSDimitry Andric 
29705ffd83dbSDimitry Andric   formatted_raw_ostream FOS(OS);
29715ffd83dbSDimitry Andric   FOS << HotFuncCount << " out of " << TotalFuncCount
29725ffd83dbSDimitry Andric       << " functions with profile ("
2973e8d8bef9SDimitry Andric       << format("%.2f%%",
2974e8d8bef9SDimitry Andric                 (static_cast<double>(HotFuncCount) / TotalFuncCount * 100))
29755ffd83dbSDimitry Andric       << ") are considered hot functions";
29765ffd83dbSDimitry Andric   if (!HotFuncMetric.empty())
29775ffd83dbSDimitry Andric     FOS << " (" << HotFuncMetric << ")";
29785ffd83dbSDimitry Andric   FOS << ".\n";
29795ffd83dbSDimitry Andric   FOS << HotProfCount << " out of " << TotalProfCount << " profile counts ("
29805ffd83dbSDimitry Andric       << format("%.2f%%", TotalProfPercent) << ") are from hot functions.\n";
29815ffd83dbSDimitry Andric 
29825ffd83dbSDimitry Andric   for (size_t I = 0; I < ColumnTitle.size(); ++I) {
29835ffd83dbSDimitry Andric     FOS.PadToColumn(ColumnOffset[I]);
29845ffd83dbSDimitry Andric     FOS << ColumnTitle[I];
29855ffd83dbSDimitry Andric   }
29865ffd83dbSDimitry Andric   FOS << "\n";
29875ffd83dbSDimitry Andric 
2988349cc55cSDimitry Andric   uint32_t Count = 0;
2989349cc55cSDimitry Andric   for (const auto &R : PrintValues) {
2990349cc55cSDimitry Andric     if (TopNFunctions && (Count++ == TopNFunctions))
2991349cc55cSDimitry Andric       break;
29925ffd83dbSDimitry Andric     FOS.PadToColumn(ColumnOffset[0]);
29935ffd83dbSDimitry Andric     FOS << R.TotalCount << " (" << format("%.2f%%", R.TotalCountPercent) << ")";
29945ffd83dbSDimitry Andric     FOS.PadToColumn(ColumnOffset[1]);
29955ffd83dbSDimitry Andric     FOS << R.MaxCount;
29965ffd83dbSDimitry Andric     FOS.PadToColumn(ColumnOffset[2]);
29975ffd83dbSDimitry Andric     FOS << R.EntryCount;
29985ffd83dbSDimitry Andric     FOS.PadToColumn(ColumnOffset[3]);
29995ffd83dbSDimitry Andric     FOS << R.FuncName << "\n";
30005ffd83dbSDimitry Andric   }
30015ffd83dbSDimitry Andric }
30025ffd83dbSDimitry Andric 
showHotFunctionList(const sampleprof::SampleProfileMap & Profiles,ProfileSummary & PS,uint32_t TopN,raw_fd_ostream & OS)3003349cc55cSDimitry Andric static int showHotFunctionList(const sampleprof::SampleProfileMap &Profiles,
3004349cc55cSDimitry Andric                                ProfileSummary &PS, uint32_t TopN,
3005349cc55cSDimitry Andric                                raw_fd_ostream &OS) {
30065ffd83dbSDimitry Andric   using namespace sampleprof;
30075ffd83dbSDimitry Andric 
30085ffd83dbSDimitry Andric   const uint32_t HotFuncCutoff = 990000;
30095ffd83dbSDimitry Andric   auto &SummaryVector = PS.getDetailedSummary();
30105ffd83dbSDimitry Andric   uint64_t MinCountThreshold = 0;
30115ffd83dbSDimitry Andric   for (const ProfileSummaryEntry &SummaryEntry : SummaryVector) {
30125ffd83dbSDimitry Andric     if (SummaryEntry.Cutoff == HotFuncCutoff) {
30135ffd83dbSDimitry Andric       MinCountThreshold = SummaryEntry.MinCount;
30145ffd83dbSDimitry Andric       break;
30155ffd83dbSDimitry Andric     }
30165ffd83dbSDimitry Andric   }
30175ffd83dbSDimitry Andric 
30185ffd83dbSDimitry Andric   // Traverse all functions in the profile and keep only hot functions.
30195ffd83dbSDimitry Andric   // The following loop also calculates the sum of total samples of all
30205ffd83dbSDimitry Andric   // functions.
30215ffd83dbSDimitry Andric   std::multimap<uint64_t, std::pair<const FunctionSamples *, const uint64_t>,
30225ffd83dbSDimitry Andric                 std::greater<uint64_t>>
30235ffd83dbSDimitry Andric       HotFunc;
30245ffd83dbSDimitry Andric   uint64_t ProfileTotalSample = 0;
30255ffd83dbSDimitry Andric   uint64_t HotFuncSample = 0;
30265ffd83dbSDimitry Andric   uint64_t HotFuncCount = 0;
3027e8d8bef9SDimitry Andric 
30285ffd83dbSDimitry Andric   for (const auto &I : Profiles) {
3029e8d8bef9SDimitry Andric     FuncSampleStats FuncStats;
30305ffd83dbSDimitry Andric     const FunctionSamples &FuncProf = I.second;
30315ffd83dbSDimitry Andric     ProfileTotalSample += FuncProf.getTotalSamples();
3032e8d8bef9SDimitry Andric     getFuncSampleStats(FuncProf, FuncStats, MinCountThreshold);
30335ffd83dbSDimitry Andric 
3034e8d8bef9SDimitry Andric     if (isFunctionHot(FuncStats, MinCountThreshold)) {
30355ffd83dbSDimitry Andric       HotFunc.emplace(FuncProf.getTotalSamples(),
3036e8d8bef9SDimitry Andric                       std::make_pair(&(I.second), FuncStats.MaxSample));
30375ffd83dbSDimitry Andric       HotFuncSample += FuncProf.getTotalSamples();
30385ffd83dbSDimitry Andric       ++HotFuncCount;
30395ffd83dbSDimitry Andric     }
30405ffd83dbSDimitry Andric   }
30415ffd83dbSDimitry Andric 
30425ffd83dbSDimitry Andric   std::vector<std::string> ColumnTitle{"Total sample (%)", "Max sample",
30435ffd83dbSDimitry Andric                                        "Entry sample", "Function name"};
30445ffd83dbSDimitry Andric   std::vector<int> ColumnOffset{0, 24, 42, 58};
30455ffd83dbSDimitry Andric   std::string Metric =
30465ffd83dbSDimitry Andric       std::string("max sample >= ") + std::to_string(MinCountThreshold);
30475ffd83dbSDimitry Andric   std::vector<HotFuncInfo> PrintValues;
30485ffd83dbSDimitry Andric   for (const auto &FuncPair : HotFunc) {
30495ffd83dbSDimitry Andric     const FunctionSamples &Func = *FuncPair.second.first;
30505ffd83dbSDimitry Andric     double TotalSamplePercent =
30515ffd83dbSDimitry Andric         (ProfileTotalSample > 0)
30525ffd83dbSDimitry Andric             ? (Func.getTotalSamples() * 100.0) / ProfileTotalSample
30535ffd83dbSDimitry Andric             : 0;
3054fcaf7f86SDimitry Andric     PrintValues.emplace_back(
3055fcaf7f86SDimitry Andric         HotFuncInfo(Func.getContext().toString(), Func.getTotalSamples(),
3056fcaf7f86SDimitry Andric                     TotalSamplePercent, FuncPair.second.second,
3057fcaf7f86SDimitry Andric                     Func.getHeadSamplesEstimate()));
30585ffd83dbSDimitry Andric   }
30595ffd83dbSDimitry Andric   dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount,
30605ffd83dbSDimitry Andric                       Profiles.size(), HotFuncSample, ProfileTotalSample,
3061349cc55cSDimitry Andric                       Metric, TopN, OS);
30625ffd83dbSDimitry Andric 
30635ffd83dbSDimitry Andric   return 0;
30645ffd83dbSDimitry Andric }
30655ffd83dbSDimitry Andric 
showSampleProfile(ShowFormat SFormat,raw_fd_ostream & OS)30665f757f3fSDimitry Andric static int showSampleProfile(ShowFormat SFormat, raw_fd_ostream &OS) {
3067bdd1243dSDimitry Andric   if (SFormat == ShowFormat::Yaml)
3068bdd1243dSDimitry Andric     exitWithError("YAML output is not supported for sample profiles");
30690b57cec5SDimitry Andric   using namespace sampleprof;
30700b57cec5SDimitry Andric   LLVMContext Context;
307106c3fb27SDimitry Andric   auto FS = vfs::getRealFileSystem();
307206c3fb27SDimitry Andric   auto ReaderOrErr = SampleProfileReader::create(Filename, Context, *FS,
307306c3fb27SDimitry Andric                                                  FSDiscriminatorPassOption);
30740b57cec5SDimitry Andric   if (std::error_code EC = ReaderOrErr.getError())
30750b57cec5SDimitry Andric     exitWithErrorCode(EC, Filename);
30760b57cec5SDimitry Andric 
30770b57cec5SDimitry Andric   auto Reader = std::move(ReaderOrErr.get());
30788bcb0991SDimitry Andric   if (ShowSectionInfoOnly) {
30798bcb0991SDimitry Andric     showSectionInfo(Reader.get(), OS);
30808bcb0991SDimitry Andric     return 0;
30818bcb0991SDimitry Andric   }
30828bcb0991SDimitry Andric 
30830b57cec5SDimitry Andric   if (std::error_code EC = Reader->read())
30840b57cec5SDimitry Andric     exitWithErrorCode(EC, Filename);
30850b57cec5SDimitry Andric 
30865f757f3fSDimitry Andric   if (ShowAllFunctions || FuncNameFilter.empty()) {
3087bdd1243dSDimitry Andric     if (SFormat == ShowFormat::Json)
3088bdd1243dSDimitry Andric       Reader->dumpJson(OS);
30890b57cec5SDimitry Andric     else
3090bdd1243dSDimitry Andric       Reader->dump(OS);
3091bdd1243dSDimitry Andric   } else {
3092bdd1243dSDimitry Andric     if (SFormat == ShowFormat::Json)
3093bdd1243dSDimitry Andric       exitWithError(
3094bdd1243dSDimitry Andric           "the JSON format is supported only when all functions are to "
3095bdd1243dSDimitry Andric           "be printed");
3096bdd1243dSDimitry Andric 
3097349cc55cSDimitry Andric     // TODO: parse context string to support filtering by contexts.
30985f757f3fSDimitry Andric     FunctionSamples *FS = Reader->getSamplesFor(StringRef(FuncNameFilter));
30995f757f3fSDimitry Andric     Reader->dumpFunctionProfile(FS ? *FS : FunctionSamples(), OS);
3100bdd1243dSDimitry Andric   }
31010b57cec5SDimitry Andric 
31028bcb0991SDimitry Andric   if (ShowProfileSymbolList) {
31038bcb0991SDimitry Andric     std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =
31048bcb0991SDimitry Andric         Reader->getProfileSymbolList();
31058bcb0991SDimitry Andric     ReaderList->dump(OS);
31068bcb0991SDimitry Andric   }
31078bcb0991SDimitry Andric 
31085ffd83dbSDimitry Andric   if (ShowDetailedSummary) {
31095ffd83dbSDimitry Andric     auto &PS = Reader->getSummary();
31105ffd83dbSDimitry Andric     PS.printSummary(OS);
31115ffd83dbSDimitry Andric     PS.printDetailedSummary(OS);
31125ffd83dbSDimitry Andric   }
31135ffd83dbSDimitry Andric 
31145f757f3fSDimitry Andric   if (ShowHotFuncList || TopNFunctions)
31155f757f3fSDimitry Andric     showHotFunctionList(Reader->getProfiles(), Reader->getSummary(),
31165f757f3fSDimitry Andric                         TopNFunctions, OS);
31175ffd83dbSDimitry Andric 
31180b57cec5SDimitry Andric   return 0;
31190b57cec5SDimitry Andric }
31200b57cec5SDimitry Andric 
showMemProfProfile(ShowFormat SFormat,raw_fd_ostream & OS)31215f757f3fSDimitry Andric static int showMemProfProfile(ShowFormat SFormat, raw_fd_ostream &OS) {
3122bdd1243dSDimitry Andric   if (SFormat == ShowFormat::Json)
3123bdd1243dSDimitry Andric     exitWithError("JSON output is not supported for MemProf");
312481ad6265SDimitry Andric   auto ReaderOr = llvm::memprof::RawMemProfReader::create(
312581ad6265SDimitry Andric       Filename, ProfiledBinary, /*KeepNames=*/true);
31264824e7fdSDimitry Andric   if (Error E = ReaderOr.takeError())
312781ad6265SDimitry Andric     // Since the error can be related to the profile or the binary we do not
312881ad6265SDimitry Andric     // pass whence. Instead additional context is provided where necessary in
312981ad6265SDimitry Andric     // the error message.
313081ad6265SDimitry Andric     exitWithError(std::move(E), /*Whence*/ "");
31314824e7fdSDimitry Andric 
31324824e7fdSDimitry Andric   std::unique_ptr<llvm::memprof::RawMemProfReader> Reader(
31334824e7fdSDimitry Andric       ReaderOr.get().release());
313481ad6265SDimitry Andric 
313581ad6265SDimitry Andric   Reader->printYAML(OS);
31364824e7fdSDimitry Andric   return 0;
31374824e7fdSDimitry Andric }
31384824e7fdSDimitry Andric 
showDebugInfoCorrelation(const std::string & Filename,ShowFormat SFormat,raw_fd_ostream & OS)313904eeddc0SDimitry Andric static int showDebugInfoCorrelation(const std::string &Filename,
3140bdd1243dSDimitry Andric                                     ShowFormat SFormat, raw_fd_ostream &OS) {
3141bdd1243dSDimitry Andric   if (SFormat == ShowFormat::Json)
3142bdd1243dSDimitry Andric     exitWithError("JSON output is not supported for debug info correlation");
314304eeddc0SDimitry Andric   std::unique_ptr<InstrProfCorrelator> Correlator;
31445f757f3fSDimitry Andric   if (auto Err =
31455f757f3fSDimitry Andric           InstrProfCorrelator::get(Filename, InstrProfCorrelator::DEBUG_INFO)
31465f757f3fSDimitry Andric               .moveInto(Correlator))
314704eeddc0SDimitry Andric     exitWithError(std::move(Err), Filename);
3148bdd1243dSDimitry Andric   if (SFormat == ShowFormat::Yaml) {
31495f757f3fSDimitry Andric     if (auto Err = Correlator->dumpYaml(MaxDbgCorrelationWarnings, OS))
3150bdd1243dSDimitry Andric       exitWithError(std::move(Err), Filename);
3151bdd1243dSDimitry Andric     return 0;
3152bdd1243dSDimitry Andric   }
3153bdd1243dSDimitry Andric 
31545f757f3fSDimitry Andric   if (auto Err = Correlator->correlateProfileData(MaxDbgCorrelationWarnings))
315504eeddc0SDimitry Andric     exitWithError(std::move(Err), Filename);
315604eeddc0SDimitry Andric 
315704eeddc0SDimitry Andric   InstrProfSymtab Symtab;
315804eeddc0SDimitry Andric   if (auto Err = Symtab.create(
315904eeddc0SDimitry Andric           StringRef(Correlator->getNamesPointer(), Correlator->getNamesSize())))
316004eeddc0SDimitry Andric     exitWithError(std::move(Err), Filename);
316104eeddc0SDimitry Andric 
316204eeddc0SDimitry Andric   if (ShowProfileSymbolList)
316304eeddc0SDimitry Andric     Symtab.dumpNames(OS);
316404eeddc0SDimitry Andric   // TODO: Read "Profile Data Type" from debug info to compute and show how many
316504eeddc0SDimitry Andric   // counters the section holds.
316604eeddc0SDimitry Andric   if (ShowDetailedSummary)
316704eeddc0SDimitry Andric     OS << "Counters section size: 0x"
316804eeddc0SDimitry Andric        << Twine::utohexstr(Correlator->getCountersSectionSize()) << " bytes\n";
316904eeddc0SDimitry Andric   OS << "Found " << Correlator->getDataSize() << " functions\n";
317004eeddc0SDimitry Andric 
317104eeddc0SDimitry Andric   return 0;
317204eeddc0SDimitry Andric }
317304eeddc0SDimitry Andric 
show_main(int argc,const char * argv[])31740b57cec5SDimitry Andric static int show_main(int argc, const char *argv[]) {
317504eeddc0SDimitry Andric   if (Filename.empty() && DebugInfoFilename.empty())
317604eeddc0SDimitry Andric     exitWithError(
317704eeddc0SDimitry Andric         "the positional argument '<profdata-file>' is required unless '--" +
317804eeddc0SDimitry Andric         DebugInfoFilename.ArgStr + "' is provided");
317904eeddc0SDimitry Andric 
3180e8d8bef9SDimitry Andric   if (Filename == OutputFilename) {
31815f757f3fSDimitry Andric     errs() << sys::path::filename(argv[0]) << " " << argv[1]
31820b57cec5SDimitry Andric            << ": Input file name cannot be the same as the output file name!\n";
31830b57cec5SDimitry Andric     return 1;
31840b57cec5SDimitry Andric   }
3185bdd1243dSDimitry Andric   if (JsonFormat)
3186bdd1243dSDimitry Andric     SFormat = ShowFormat::Json;
31870b57cec5SDimitry Andric 
31880b57cec5SDimitry Andric   std::error_code EC;
3189fe6060f1SDimitry Andric   raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);
31900b57cec5SDimitry Andric   if (EC)
31910b57cec5SDimitry Andric     exitWithErrorCode(EC, OutputFilename);
31920b57cec5SDimitry Andric 
31935f757f3fSDimitry Andric   if (ShowAllFunctions && !FuncNameFilter.empty())
31940b57cec5SDimitry Andric     WithColor::warning() << "-function argument ignored: showing all functions\n";
31950b57cec5SDimitry Andric 
319604eeddc0SDimitry Andric   if (!DebugInfoFilename.empty())
31975f757f3fSDimitry Andric     return showDebugInfoCorrelation(DebugInfoFilename, SFormat, OS);
319804eeddc0SDimitry Andric 
31995f757f3fSDimitry Andric   if (ShowProfileKind == instr)
32005f757f3fSDimitry Andric     return showInstrProfile(SFormat, OS);
32015f757f3fSDimitry Andric   if (ShowProfileKind == sample)
32025f757f3fSDimitry Andric     return showSampleProfile(SFormat, OS);
32035f757f3fSDimitry Andric   return showMemProfProfile(SFormat, OS);
32040b57cec5SDimitry Andric }
32050b57cec5SDimitry Andric 
order_main(int argc,const char * argv[])320606c3fb27SDimitry Andric static int order_main(int argc, const char *argv[]) {
320706c3fb27SDimitry Andric   std::error_code EC;
320806c3fb27SDimitry Andric   raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);
320906c3fb27SDimitry Andric   if (EC)
321006c3fb27SDimitry Andric     exitWithErrorCode(EC, OutputFilename);
321106c3fb27SDimitry Andric   auto FS = vfs::getRealFileSystem();
321206c3fb27SDimitry Andric   auto ReaderOrErr = InstrProfReader::create(Filename, *FS);
321306c3fb27SDimitry Andric   if (Error E = ReaderOrErr.takeError())
321406c3fb27SDimitry Andric     exitWithError(std::move(E), Filename);
321506c3fb27SDimitry Andric 
321606c3fb27SDimitry Andric   auto Reader = std::move(ReaderOrErr.get());
321706c3fb27SDimitry Andric   for (auto &I : *Reader) {
321806c3fb27SDimitry Andric     // Read all entries
321906c3fb27SDimitry Andric     (void)I;
322006c3fb27SDimitry Andric   }
322106c3fb27SDimitry Andric   auto &Traces = Reader->getTemporalProfTraces();
322206c3fb27SDimitry Andric   auto Nodes = TemporalProfTraceTy::createBPFunctionNodes(Traces);
322306c3fb27SDimitry Andric   BalancedPartitioningConfig Config;
322406c3fb27SDimitry Andric   BalancedPartitioning BP(Config);
322506c3fb27SDimitry Andric   BP.run(Nodes);
322606c3fb27SDimitry Andric 
32271db9f3b2SDimitry Andric   OS << "# Ordered " << Nodes.size() << " functions\n";
32281db9f3b2SDimitry Andric   OS << "# Warning: Mach-O may prefix symbols with \"_\" depending on the "
32291db9f3b2SDimitry Andric         "linkage and this output does not take that into account. Some "
32301db9f3b2SDimitry Andric         "post-processing may be required before passing to the linker via "
32311db9f3b2SDimitry Andric         "-order_file.\n";
323206c3fb27SDimitry Andric   for (auto &N : Nodes) {
32335f757f3fSDimitry Andric     auto [Filename, ParsedFuncName] =
32345f757f3fSDimitry Andric         getParsedIRPGOFuncName(Reader->getSymtab().getFuncOrVarName(N.Id));
32355f757f3fSDimitry Andric     if (!Filename.empty())
323606c3fb27SDimitry Andric       OS << "# " << Filename << "\n";
32375f757f3fSDimitry Andric     OS << ParsedFuncName << "\n";
323806c3fb27SDimitry Andric   }
323906c3fb27SDimitry Andric   return 0;
324006c3fb27SDimitry Andric }
324106c3fb27SDimitry Andric 
llvm_profdata_main(int argc,char ** argvNonConst,const llvm::ToolContext &)324206c3fb27SDimitry Andric int llvm_profdata_main(int argc, char **argvNonConst,
324306c3fb27SDimitry Andric                        const llvm::ToolContext &) {
3244bdd1243dSDimitry Andric   const char **argv = const_cast<const char **>(argvNonConst);
32450b57cec5SDimitry Andric 
32460b57cec5SDimitry Andric   StringRef ProgName(sys::path::filename(argv[0]));
32470b57cec5SDimitry Andric 
32485f757f3fSDimitry Andric   if (argc < 2) {
32495f757f3fSDimitry Andric     errs() << ProgName
32505f757f3fSDimitry Andric            << ": No subcommand specified! Run llvm-profata --help for usage.\n";
32515f757f3fSDimitry Andric     return 1;
32520b57cec5SDimitry Andric   }
32530b57cec5SDimitry Andric 
32545f757f3fSDimitry Andric   cl::ParseCommandLineOptions(argc, argv, "LLVM profile data\n");
32550b57cec5SDimitry Andric 
32565f757f3fSDimitry Andric   if (ShowSubcommand)
32575f757f3fSDimitry Andric     return show_main(argc, argv);
325806c3fb27SDimitry Andric 
32595f757f3fSDimitry Andric   if (OrderSubcommand)
32605f757f3fSDimitry Andric     return order_main(argc, argv);
32610b57cec5SDimitry Andric 
32625f757f3fSDimitry Andric   if (OverlapSubcommand)
32635f757f3fSDimitry Andric     return overlap_main(argc, argv);
32640b57cec5SDimitry Andric 
32655f757f3fSDimitry Andric   if (MergeSubcommand)
32665f757f3fSDimitry Andric     return merge_main(argc, argv);
32675f757f3fSDimitry Andric 
32685f757f3fSDimitry Andric   errs() << ProgName
32695f757f3fSDimitry Andric          << ": Unknown command. Run llvm-profdata --help for usage.\n";
32700b57cec5SDimitry Andric   return 1;
32710b57cec5SDimitry Andric }
3272