xref: /freebsd/contrib/llvm-project/llvm/tools/llvm-profdata/llvm-profdata.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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"
22*0fca6ea1SDimitry Andric #include "llvm/ProfileData/MemProfReader.h"
230b57cec5SDimitry Andric #include "llvm/ProfileData/ProfileCommon.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 };
78bdd1243dSDimitry Andric 
790b57cec5SDimitry Andric enum ProfileFormat {
800b57cec5SDimitry Andric   PF_None = 0,
810b57cec5SDimitry Andric   PF_Text,
8206c3fb27SDimitry Andric   PF_Compact_Binary, // Deprecated
838bcb0991SDimitry Andric   PF_Ext_Binary,
840b57cec5SDimitry Andric   PF_GCC,
850b57cec5SDimitry Andric   PF_Binary
860b57cec5SDimitry Andric };
870b57cec5SDimitry Andric 
88bdd1243dSDimitry Andric enum class ShowFormat { Text, Json, Yaml };
89*0fca6ea1SDimitry Andric } // namespace
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 
294*0fca6ea1SDimitry Andric cl::opt<bool> KeepVTableSymbols(
295*0fca6ea1SDimitry Andric     "keep-vtable-symbols", cl::init(false), cl::Hidden,
296*0fca6ea1SDimitry Andric     cl::sub(MergeSubcommand),
297*0fca6ea1SDimitry Andric     cl::desc("If true, keep the vtable symbols in indexed profiles"));
298*0fca6ea1SDimitry Andric 
299*0fca6ea1SDimitry Andric // Temporary support for writing the previous version of the format, to enable
300*0fca6ea1SDimitry Andric // some forward compatibility.
301*0fca6ea1SDimitry Andric // TODO: Consider enabling this with future version changes as well, to ease
302*0fca6ea1SDimitry Andric // deployment of newer versions of llvm-profdata.
303*0fca6ea1SDimitry Andric cl::opt<bool> DoWritePrevVersion(
304*0fca6ea1SDimitry Andric     "write-prev-version", cl::init(false), cl::Hidden,
305*0fca6ea1SDimitry Andric     cl::desc("Write the previous version of indexed format, to enable "
306*0fca6ea1SDimitry Andric              "some forward compatibility."));
307*0fca6ea1SDimitry Andric 
308*0fca6ea1SDimitry Andric cl::opt<memprof::IndexedVersion> MemProfVersionRequested(
309*0fca6ea1SDimitry Andric     "memprof-version", cl::Hidden, cl::sub(MergeSubcommand),
310*0fca6ea1SDimitry Andric     cl::desc("Specify the version of the memprof format to use"),
311*0fca6ea1SDimitry Andric     cl::init(memprof::Version0),
312*0fca6ea1SDimitry Andric     cl::values(clEnumValN(memprof::Version0, "0", "version 0"),
313*0fca6ea1SDimitry Andric                clEnumValN(memprof::Version1, "1", "version 1"),
314*0fca6ea1SDimitry Andric                clEnumValN(memprof::Version2, "2", "version 2"),
315*0fca6ea1SDimitry Andric                clEnumValN(memprof::Version3, "3", "version 3")));
316*0fca6ea1SDimitry Andric 
317*0fca6ea1SDimitry Andric cl::opt<bool> MemProfFullSchema(
318*0fca6ea1SDimitry Andric     "memprof-full-schema", cl::Hidden, cl::sub(MergeSubcommand),
319*0fca6ea1SDimitry Andric     cl::desc("Use the full schema for serialization"), cl::init(false));
320*0fca6ea1SDimitry Andric 
3215f757f3fSDimitry Andric // Options specific to overlap subcommand.
3225f757f3fSDimitry Andric cl::opt<std::string> BaseFilename(cl::Positional, cl::Required,
3235f757f3fSDimitry Andric                                   cl::desc("<base profile file>"),
3245f757f3fSDimitry Andric                                   cl::sub(OverlapSubcommand));
3255f757f3fSDimitry Andric cl::opt<std::string> TestFilename(cl::Positional, cl::Required,
3265f757f3fSDimitry Andric                                   cl::desc("<test profile file>"),
3275f757f3fSDimitry Andric                                   cl::sub(OverlapSubcommand));
3285f757f3fSDimitry Andric 
3295f757f3fSDimitry Andric cl::opt<unsigned long long> SimilarityCutoff(
3305f757f3fSDimitry Andric     "similarity-cutoff", cl::init(0),
3315f757f3fSDimitry Andric     cl::desc("For sample profiles, list function names (with calling context "
3325f757f3fSDimitry Andric              "for csspgo) for overlapped functions "
3335f757f3fSDimitry Andric              "with similarities below the cutoff (percentage times 10000)."),
3345f757f3fSDimitry Andric     cl::sub(OverlapSubcommand));
3355f757f3fSDimitry Andric 
3365f757f3fSDimitry Andric cl::opt<bool> IsCS(
3375f757f3fSDimitry Andric     "cs", cl::init(false),
3385f757f3fSDimitry Andric     cl::desc("For context sensitive PGO counts. Does not work with CSSPGO."),
3395f757f3fSDimitry Andric     cl::sub(OverlapSubcommand));
3405f757f3fSDimitry Andric 
3415f757f3fSDimitry Andric cl::opt<unsigned long long> OverlapValueCutoff(
3425f757f3fSDimitry Andric     "value-cutoff", cl::init(-1),
3435f757f3fSDimitry Andric     cl::desc(
3445f757f3fSDimitry Andric         "Function level overlap information for every function (with calling "
3455f757f3fSDimitry Andric         "context for csspgo) in test "
3465f757f3fSDimitry Andric         "profile with max count value greater then the parameter value"),
3475f757f3fSDimitry Andric     cl::sub(OverlapSubcommand));
3485f757f3fSDimitry Andric 
349*0fca6ea1SDimitry Andric // Options specific to show subcommand.
3505f757f3fSDimitry Andric cl::opt<bool> ShowCounts("counts", cl::init(false),
3515f757f3fSDimitry Andric                          cl::desc("Show counter values for shown functions"),
3525f757f3fSDimitry Andric                          cl::sub(ShowSubcommand));
3535f757f3fSDimitry Andric cl::opt<ShowFormat>
3545f757f3fSDimitry Andric     SFormat("show-format", cl::init(ShowFormat::Text),
3555f757f3fSDimitry Andric             cl::desc("Emit output in the selected format if supported"),
3565f757f3fSDimitry Andric             cl::sub(ShowSubcommand),
3575f757f3fSDimitry Andric             cl::values(clEnumValN(ShowFormat::Text, "text",
3585f757f3fSDimitry Andric                                   "emit normal text output (default)"),
3595f757f3fSDimitry Andric                        clEnumValN(ShowFormat::Json, "json", "emit JSON"),
3605f757f3fSDimitry Andric                        clEnumValN(ShowFormat::Yaml, "yaml", "emit YAML")));
3615f757f3fSDimitry Andric // TODO: Consider replacing this with `--show-format=text-encoding`.
3625f757f3fSDimitry Andric cl::opt<bool>
3635f757f3fSDimitry Andric     TextFormat("text", cl::init(false),
3645f757f3fSDimitry Andric                cl::desc("Show instr profile data in text dump format"),
3655f757f3fSDimitry Andric                cl::sub(ShowSubcommand));
3665f757f3fSDimitry Andric cl::opt<bool>
3675f757f3fSDimitry Andric     JsonFormat("json",
3685f757f3fSDimitry Andric                cl::desc("Show sample profile data in the JSON format "
3695f757f3fSDimitry Andric                         "(deprecated, please use --show-format=json)"),
3705f757f3fSDimitry Andric                cl::sub(ShowSubcommand));
3715f757f3fSDimitry Andric cl::opt<bool> ShowIndirectCallTargets(
3725f757f3fSDimitry Andric     "ic-targets", cl::init(false),
3735f757f3fSDimitry Andric     cl::desc("Show indirect call site target values for shown functions"),
3745f757f3fSDimitry Andric     cl::sub(ShowSubcommand));
375*0fca6ea1SDimitry Andric cl::opt<bool> ShowVTables("show-vtables", cl::init(false),
376*0fca6ea1SDimitry Andric                           cl::desc("Show vtable names for shown functions"),
377*0fca6ea1SDimitry Andric                           cl::sub(ShowSubcommand));
3785f757f3fSDimitry Andric cl::opt<bool> ShowMemOPSizes(
3795f757f3fSDimitry Andric     "memop-sizes", cl::init(false),
3805f757f3fSDimitry Andric     cl::desc("Show the profiled sizes of the memory intrinsic calls "
3815f757f3fSDimitry Andric              "for shown functions"),
3825f757f3fSDimitry Andric     cl::sub(ShowSubcommand));
3835f757f3fSDimitry Andric cl::opt<bool> ShowDetailedSummary("detailed-summary", cl::init(false),
3845f757f3fSDimitry Andric                                   cl::desc("Show detailed profile summary"),
3855f757f3fSDimitry Andric                                   cl::sub(ShowSubcommand));
3865f757f3fSDimitry Andric cl::list<uint32_t> DetailedSummaryCutoffs(
3875f757f3fSDimitry Andric     cl::CommaSeparated, "detailed-summary-cutoffs",
3885f757f3fSDimitry Andric     cl::desc(
3895f757f3fSDimitry Andric         "Cutoff percentages (times 10000) for generating detailed summary"),
3905f757f3fSDimitry Andric     cl::value_desc("800000,901000,999999"), cl::sub(ShowSubcommand));
3915f757f3fSDimitry Andric cl::opt<bool>
3925f757f3fSDimitry Andric     ShowHotFuncList("hot-func-list", cl::init(false),
3935f757f3fSDimitry Andric                     cl::desc("Show profile summary of a list of hot functions"),
3945f757f3fSDimitry Andric                     cl::sub(ShowSubcommand));
3955f757f3fSDimitry Andric cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false),
3965f757f3fSDimitry Andric                                cl::desc("Details for each and every function"),
3975f757f3fSDimitry Andric                                cl::sub(ShowSubcommand));
3985f757f3fSDimitry Andric cl::opt<bool> ShowCS("showcs", cl::init(false),
3995f757f3fSDimitry Andric                      cl::desc("Show context sensitive counts"),
4005f757f3fSDimitry Andric                      cl::sub(ShowSubcommand));
4015f757f3fSDimitry Andric cl::opt<ProfileKinds> ShowProfileKind(
4025f757f3fSDimitry Andric     cl::desc("Profile kind supported by show:"), cl::sub(ShowSubcommand),
4035f757f3fSDimitry Andric     cl::init(instr),
4045f757f3fSDimitry Andric     cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
4055f757f3fSDimitry Andric                clEnumVal(sample, "Sample profile"),
4065f757f3fSDimitry Andric                clEnumVal(memory, "MemProf memory access profile")));
4075f757f3fSDimitry Andric cl::opt<uint32_t> TopNFunctions(
4085f757f3fSDimitry Andric     "topn", cl::init(0),
4095f757f3fSDimitry Andric     cl::desc("Show the list of functions with the largest internal counts"),
4105f757f3fSDimitry Andric     cl::sub(ShowSubcommand));
4115f757f3fSDimitry Andric cl::opt<uint32_t> ShowValueCutoff(
4125f757f3fSDimitry Andric     "value-cutoff", cl::init(0),
4135f757f3fSDimitry Andric     cl::desc("Set the count value cutoff. Functions with the maximum count "
4145f757f3fSDimitry Andric              "less than this value will not be printed out. (Default is 0)"),
4155f757f3fSDimitry Andric     cl::sub(ShowSubcommand));
4165f757f3fSDimitry Andric cl::opt<bool> OnlyListBelow(
4175f757f3fSDimitry Andric     "list-below-cutoff", cl::init(false),
4185f757f3fSDimitry Andric     cl::desc("Only output names of functions whose max count values are "
4195f757f3fSDimitry Andric              "below the cutoff value"),
4205f757f3fSDimitry Andric     cl::sub(ShowSubcommand));
4215f757f3fSDimitry Andric cl::opt<bool> ShowProfileSymbolList(
4225f757f3fSDimitry Andric     "show-prof-sym-list", cl::init(false),
4235f757f3fSDimitry Andric     cl::desc("Show profile symbol list if it exists in the profile. "),
4245f757f3fSDimitry Andric     cl::sub(ShowSubcommand));
4255f757f3fSDimitry Andric cl::opt<bool> ShowSectionInfoOnly(
4265f757f3fSDimitry Andric     "show-sec-info-only", cl::init(false),
4275f757f3fSDimitry Andric     cl::desc("Show the information of each section in the sample profile. "
4285f757f3fSDimitry Andric              "The flag is only usable when the sample profile is in "
4295f757f3fSDimitry Andric              "extbinary format"),
4305f757f3fSDimitry Andric     cl::sub(ShowSubcommand));
4315f757f3fSDimitry Andric cl::opt<bool> ShowBinaryIds("binary-ids", cl::init(false),
4325f757f3fSDimitry Andric                             cl::desc("Show binary ids in the profile. "),
4335f757f3fSDimitry Andric                             cl::sub(ShowSubcommand));
4345f757f3fSDimitry Andric cl::opt<bool> ShowTemporalProfTraces(
4355f757f3fSDimitry Andric     "temporal-profile-traces",
4365f757f3fSDimitry Andric     cl::desc("Show temporal profile traces in the profile."),
4375f757f3fSDimitry Andric     cl::sub(ShowSubcommand));
4385f757f3fSDimitry Andric 
4395f757f3fSDimitry Andric cl::opt<bool>
4405f757f3fSDimitry Andric     ShowCovered("covered", cl::init(false),
4415f757f3fSDimitry Andric                 cl::desc("Show only the functions that have been executed."),
4425f757f3fSDimitry Andric                 cl::sub(ShowSubcommand));
4435f757f3fSDimitry Andric 
4445f757f3fSDimitry Andric cl::opt<bool> ShowProfileVersion("profile-version", cl::init(false),
4455f757f3fSDimitry Andric                                  cl::desc("Show profile version. "),
4465f757f3fSDimitry Andric                                  cl::sub(ShowSubcommand));
4475f757f3fSDimitry Andric 
448*0fca6ea1SDimitry Andric // Options specific to order subcommand.
449*0fca6ea1SDimitry Andric cl::opt<unsigned>
450*0fca6ea1SDimitry Andric     NumTestTraces("num-test-traces", cl::init(0),
451*0fca6ea1SDimitry Andric                   cl::desc("Keep aside the last <num-test-traces> traces in "
452*0fca6ea1SDimitry Andric                            "the profile when computing the function order and "
453*0fca6ea1SDimitry Andric                            "instead use them to evaluate that order"),
454*0fca6ea1SDimitry Andric                   cl::sub(OrderSubcommand));
455*0fca6ea1SDimitry Andric 
4565f757f3fSDimitry Andric // We use this string to indicate that there are
4575f757f3fSDimitry Andric // multiple static functions map to the same name.
4585f757f3fSDimitry Andric const std::string DuplicateNameStr = "----";
4595f757f3fSDimitry Andric 
warn(Twine Message,StringRef Whence="",StringRef Hint="")460*0fca6ea1SDimitry Andric static void warn(Twine Message, StringRef Whence = "", StringRef Hint = "") {
4610b57cec5SDimitry Andric   WithColor::warning();
4620b57cec5SDimitry Andric   if (!Whence.empty())
4630b57cec5SDimitry Andric     errs() << Whence << ": ";
4640b57cec5SDimitry Andric   errs() << Message << "\n";
4650b57cec5SDimitry Andric   if (!Hint.empty())
4660b57cec5SDimitry Andric     WithColor::note() << Hint << "\n";
4670b57cec5SDimitry Andric }
4680b57cec5SDimitry Andric 
warn(Error E,StringRef Whence="")469fe6060f1SDimitry Andric static void warn(Error E, StringRef Whence = "") {
470fe6060f1SDimitry Andric   if (E.isA<InstrProfError>()) {
471fe6060f1SDimitry Andric     handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
472*0fca6ea1SDimitry Andric       warn(IPE.message(), Whence);
473fe6060f1SDimitry Andric     });
474fe6060f1SDimitry Andric   }
475fe6060f1SDimitry Andric }
476fe6060f1SDimitry Andric 
exitWithError(Twine Message,StringRef Whence="",StringRef Hint="")477*0fca6ea1SDimitry Andric static void exitWithError(Twine Message, StringRef Whence = "",
478*0fca6ea1SDimitry Andric                           StringRef Hint = "") {
4790b57cec5SDimitry Andric   WithColor::error();
4800b57cec5SDimitry Andric   if (!Whence.empty())
4810b57cec5SDimitry Andric     errs() << Whence << ": ";
4820b57cec5SDimitry Andric   errs() << Message << "\n";
4830b57cec5SDimitry Andric   if (!Hint.empty())
4840b57cec5SDimitry Andric     WithColor::note() << Hint << "\n";
4850b57cec5SDimitry Andric   ::exit(1);
4860b57cec5SDimitry Andric }
4870b57cec5SDimitry Andric 
exitWithError(Error E,StringRef Whence="")4880b57cec5SDimitry Andric static void exitWithError(Error E, StringRef Whence = "") {
4890b57cec5SDimitry Andric   if (E.isA<InstrProfError>()) {
4900b57cec5SDimitry Andric     handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
4910b57cec5SDimitry Andric       instrprof_error instrError = IPE.get();
4920b57cec5SDimitry Andric       StringRef Hint = "";
4930b57cec5SDimitry Andric       if (instrError == instrprof_error::unrecognized_format) {
4944824e7fdSDimitry Andric         // Hint in case user missed specifying the profile type.
4954824e7fdSDimitry Andric         Hint = "Perhaps you forgot to use the --sample or --memory option?";
4960b57cec5SDimitry Andric       }
497*0fca6ea1SDimitry Andric       exitWithError(IPE.message(), Whence, Hint);
4980b57cec5SDimitry Andric     });
49981ad6265SDimitry Andric     return;
5000b57cec5SDimitry Andric   }
5010b57cec5SDimitry Andric 
502*0fca6ea1SDimitry Andric   exitWithError(toString(std::move(E)), Whence);
5030b57cec5SDimitry Andric }
5040b57cec5SDimitry Andric 
exitWithErrorCode(std::error_code EC,StringRef Whence="")5050b57cec5SDimitry Andric static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") {
506*0fca6ea1SDimitry Andric   exitWithError(EC.message(), Whence);
5070b57cec5SDimitry Andric }
5080b57cec5SDimitry Andric 
warnOrExitGivenError(FailureMode FailMode,std::error_code EC,StringRef Whence="")5098bcb0991SDimitry Andric static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC,
5108bcb0991SDimitry Andric                                  StringRef Whence = "") {
5118bcb0991SDimitry Andric   if (FailMode == failIfAnyAreInvalid)
5128bcb0991SDimitry Andric     exitWithErrorCode(EC, Whence);
5138bcb0991SDimitry Andric   else
514*0fca6ea1SDimitry Andric     warn(EC.message(), Whence);
5150b57cec5SDimitry Andric }
5160b57cec5SDimitry Andric 
handleMergeWriterError(Error E,StringRef WhenceFile="",StringRef WhenceFunction="",bool ShowHint=true)5170b57cec5SDimitry Andric static void handleMergeWriterError(Error E, StringRef WhenceFile = "",
5180b57cec5SDimitry Andric                                    StringRef WhenceFunction = "",
5190b57cec5SDimitry Andric                                    bool ShowHint = true) {
5200b57cec5SDimitry Andric   if (!WhenceFile.empty())
5210b57cec5SDimitry Andric     errs() << WhenceFile << ": ";
5220b57cec5SDimitry Andric   if (!WhenceFunction.empty())
5230b57cec5SDimitry Andric     errs() << WhenceFunction << ": ";
5240b57cec5SDimitry Andric 
5250b57cec5SDimitry Andric   auto IPE = instrprof_error::success;
5260b57cec5SDimitry Andric   E = handleErrors(std::move(E),
5270b57cec5SDimitry Andric                    [&IPE](std::unique_ptr<InstrProfError> E) -> Error {
5280b57cec5SDimitry Andric                      IPE = E->get();
5290b57cec5SDimitry Andric                      return Error(std::move(E));
5300b57cec5SDimitry Andric                    });
5310b57cec5SDimitry Andric   errs() << toString(std::move(E)) << "\n";
5320b57cec5SDimitry Andric 
5330b57cec5SDimitry Andric   if (ShowHint) {
5340b57cec5SDimitry Andric     StringRef Hint = "";
5350b57cec5SDimitry Andric     if (IPE != instrprof_error::success) {
5360b57cec5SDimitry Andric       switch (IPE) {
5370b57cec5SDimitry Andric       case instrprof_error::hash_mismatch:
5380b57cec5SDimitry Andric       case instrprof_error::count_mismatch:
5390b57cec5SDimitry Andric       case instrprof_error::value_site_count_mismatch:
5400b57cec5SDimitry Andric         Hint = "Make sure that all profile data to be merged is generated "
5410b57cec5SDimitry Andric                "from the same binary.";
5420b57cec5SDimitry Andric         break;
5430b57cec5SDimitry Andric       default:
5440b57cec5SDimitry Andric         break;
5450b57cec5SDimitry Andric       }
5460b57cec5SDimitry Andric     }
5470b57cec5SDimitry Andric 
5480b57cec5SDimitry Andric     if (!Hint.empty())
5490b57cec5SDimitry Andric       errs() << Hint << "\n";
5500b57cec5SDimitry Andric   }
5510b57cec5SDimitry Andric }
5520b57cec5SDimitry Andric 
5530b57cec5SDimitry Andric namespace {
5540b57cec5SDimitry Andric /// A remapper from original symbol names to new symbol names based on a file
5550b57cec5SDimitry Andric /// containing a list of mappings from old name to new name.
5560b57cec5SDimitry Andric class SymbolRemapper {
5570b57cec5SDimitry Andric   std::unique_ptr<MemoryBuffer> File;
5580b57cec5SDimitry Andric   DenseMap<StringRef, StringRef> RemappingTable;
5590b57cec5SDimitry Andric 
5600b57cec5SDimitry Andric public:
5610b57cec5SDimitry Andric   /// Build a SymbolRemapper from a file containing a list of old/new symbols.
create(StringRef InputFile)5620b57cec5SDimitry Andric   static std::unique_ptr<SymbolRemapper> create(StringRef InputFile) {
5630b57cec5SDimitry Andric     auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);
5640b57cec5SDimitry Andric     if (!BufOrError)
5650b57cec5SDimitry Andric       exitWithErrorCode(BufOrError.getError(), InputFile);
5660b57cec5SDimitry Andric 
5678bcb0991SDimitry Andric     auto Remapper = std::make_unique<SymbolRemapper>();
5680b57cec5SDimitry Andric     Remapper->File = std::move(BufOrError.get());
5690b57cec5SDimitry Andric 
5700b57cec5SDimitry Andric     for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#');
5710b57cec5SDimitry Andric          !LineIt.is_at_eof(); ++LineIt) {
5720b57cec5SDimitry Andric       std::pair<StringRef, StringRef> Parts = LineIt->split(' ');
5730b57cec5SDimitry Andric       if (Parts.first.empty() || Parts.second.empty() ||
5740b57cec5SDimitry Andric           Parts.second.count(' ')) {
5750b57cec5SDimitry Andric         exitWithError("unexpected line in remapping file",
5760b57cec5SDimitry Andric                       (InputFile + ":" + Twine(LineIt.line_number())).str(),
5770b57cec5SDimitry Andric                       "expected 'old_symbol new_symbol'");
5780b57cec5SDimitry Andric       }
5790b57cec5SDimitry Andric       Remapper->RemappingTable.insert(Parts);
5800b57cec5SDimitry Andric     }
5810b57cec5SDimitry Andric     return Remapper;
5820b57cec5SDimitry Andric   }
5830b57cec5SDimitry Andric 
5840b57cec5SDimitry Andric   /// Attempt to map the given old symbol into a new symbol.
5850b57cec5SDimitry Andric   ///
5860b57cec5SDimitry Andric   /// \return The new symbol, or \p Name if no such symbol was found.
operator ()(StringRef Name)5870b57cec5SDimitry Andric   StringRef operator()(StringRef Name) {
5880b57cec5SDimitry Andric     StringRef New = RemappingTable.lookup(Name);
5890b57cec5SDimitry Andric     return New.empty() ? Name : New;
5900b57cec5SDimitry Andric   }
5915f757f3fSDimitry Andric 
operator ()(FunctionId Name)5925f757f3fSDimitry Andric   FunctionId operator()(FunctionId Name) {
5935f757f3fSDimitry Andric     // MD5 name cannot be remapped.
5945f757f3fSDimitry Andric     if (!Name.isStringRef())
5955f757f3fSDimitry Andric       return Name;
5965f757f3fSDimitry Andric     StringRef New = RemappingTable.lookup(Name.stringRef());
5975f757f3fSDimitry Andric     return New.empty() ? Name : FunctionId(New);
5985f757f3fSDimitry Andric   }
5990b57cec5SDimitry Andric };
6000b57cec5SDimitry Andric }
6010b57cec5SDimitry Andric 
6020b57cec5SDimitry Andric struct WeightedFile {
6030b57cec5SDimitry Andric   std::string Filename;
6040b57cec5SDimitry Andric   uint64_t Weight;
6050b57cec5SDimitry Andric };
6060b57cec5SDimitry Andric typedef SmallVector<WeightedFile, 5> WeightedFileVector;
6070b57cec5SDimitry Andric 
6080b57cec5SDimitry Andric /// Keep track of merged data and reported errors.
6090b57cec5SDimitry Andric struct WriterContext {
6100b57cec5SDimitry Andric   std::mutex Lock;
6110b57cec5SDimitry Andric   InstrProfWriter Writer;
6128bcb0991SDimitry Andric   std::vector<std::pair<Error, std::string>> Errors;
6130b57cec5SDimitry Andric   std::mutex &ErrLock;
6140b57cec5SDimitry Andric   SmallSet<instrprof_error, 4> &WriterErrorCodes;
6150b57cec5SDimitry Andric 
WriterContextWriterContext6160b57cec5SDimitry Andric   WriterContext(bool IsSparse, std::mutex &ErrLock,
61706c3fb27SDimitry Andric                 SmallSet<instrprof_error, 4> &WriterErrorCodes,
61806c3fb27SDimitry Andric                 uint64_t ReservoirSize = 0, uint64_t MaxTraceLength = 0)
619*0fca6ea1SDimitry Andric       : Writer(IsSparse, ReservoirSize, MaxTraceLength, DoWritePrevVersion,
620*0fca6ea1SDimitry Andric                MemProfVersionRequested, MemProfFullSchema),
621*0fca6ea1SDimitry Andric         ErrLock(ErrLock), WriterErrorCodes(WriterErrorCodes) {}
6220b57cec5SDimitry Andric };
6230b57cec5SDimitry Andric 
6240b57cec5SDimitry Andric /// Computer the overlap b/w profile BaseFilename and TestFileName,
6250b57cec5SDimitry 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)6260b57cec5SDimitry Andric static void overlapInput(const std::string &BaseFilename,
6270b57cec5SDimitry Andric                          const std::string &TestFilename, WriterContext *WC,
6280b57cec5SDimitry Andric                          OverlapStats &Overlap,
6290b57cec5SDimitry Andric                          const OverlapFuncFilters &FuncFilter,
6300b57cec5SDimitry Andric                          raw_fd_ostream &OS, bool IsCS) {
63106c3fb27SDimitry Andric   auto FS = vfs::getRealFileSystem();
63206c3fb27SDimitry Andric   auto ReaderOrErr = InstrProfReader::create(TestFilename, *FS);
6330b57cec5SDimitry Andric   if (Error E = ReaderOrErr.takeError()) {
6340b57cec5SDimitry Andric     // Skip the empty profiles by returning sliently.
63506c3fb27SDimitry Andric     auto [ErrorCode, Msg] = InstrProfError::take(std::move(E));
63606c3fb27SDimitry Andric     if (ErrorCode != instrprof_error::empty_raw_profile)
63706c3fb27SDimitry Andric       WC->Errors.emplace_back(make_error<InstrProfError>(ErrorCode, Msg),
63806c3fb27SDimitry Andric                               TestFilename);
6390b57cec5SDimitry Andric     return;
6400b57cec5SDimitry Andric   }
6410b57cec5SDimitry Andric 
6420b57cec5SDimitry Andric   auto Reader = std::move(ReaderOrErr.get());
6430b57cec5SDimitry Andric   for (auto &I : *Reader) {
6440b57cec5SDimitry Andric     OverlapStats FuncOverlap(OverlapStats::FunctionLevel);
6450b57cec5SDimitry Andric     FuncOverlap.setFuncInfo(I.Name, I.Hash);
6460b57cec5SDimitry Andric 
6470b57cec5SDimitry Andric     WC->Writer.overlapRecord(std::move(I), Overlap, FuncOverlap, FuncFilter);
6480b57cec5SDimitry Andric     FuncOverlap.dump(OS);
6490b57cec5SDimitry Andric   }
6500b57cec5SDimitry Andric }
6510b57cec5SDimitry Andric 
6520b57cec5SDimitry Andric /// Load an input into a writer context.
loadInput(const WeightedFile & Input,SymbolRemapper * Remapper,const InstrProfCorrelator * Correlator,const StringRef ProfiledBinary,WriterContext * WC)6530b57cec5SDimitry Andric static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper,
6540eae32dcSDimitry Andric                       const InstrProfCorrelator *Correlator,
65581ad6265SDimitry Andric                       const StringRef ProfiledBinary, WriterContext *WC) {
6560b57cec5SDimitry Andric   std::unique_lock<std::mutex> CtxGuard{WC->Lock};
6570b57cec5SDimitry Andric 
6580b57cec5SDimitry Andric   // Copy the filename, because llvm::ThreadPool copied the input "const
6590b57cec5SDimitry Andric   // WeightedFile &" by value, making a reference to the filename within it
6600b57cec5SDimitry Andric   // invalid outside of this packaged task.
6618bcb0991SDimitry Andric   std::string Filename = Input.Filename;
6620b57cec5SDimitry Andric 
66381ad6265SDimitry Andric   using ::llvm::memprof::RawMemProfReader;
66481ad6265SDimitry Andric   if (RawMemProfReader::hasFormat(Input.Filename)) {
66581ad6265SDimitry Andric     auto ReaderOrErr = RawMemProfReader::create(Input.Filename, ProfiledBinary);
66681ad6265SDimitry Andric     if (!ReaderOrErr) {
66781ad6265SDimitry Andric       exitWithError(ReaderOrErr.takeError(), Input.Filename);
66881ad6265SDimitry Andric     }
66981ad6265SDimitry Andric     std::unique_ptr<RawMemProfReader> Reader = std::move(ReaderOrErr.get());
67081ad6265SDimitry Andric     // Check if the profile types can be merged, e.g. clang frontend profiles
67181ad6265SDimitry Andric     // should not be merged with memprof profiles.
67281ad6265SDimitry Andric     if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) {
67381ad6265SDimitry Andric       consumeError(std::move(E));
67481ad6265SDimitry Andric       WC->Errors.emplace_back(
67581ad6265SDimitry Andric           make_error<StringError>(
67681ad6265SDimitry Andric               "Cannot merge MemProf profile with Clang generated profile.",
67781ad6265SDimitry Andric               std::error_code()),
67881ad6265SDimitry Andric           Filename);
67981ad6265SDimitry Andric       return;
68081ad6265SDimitry Andric     }
68181ad6265SDimitry Andric 
68281ad6265SDimitry Andric     auto MemProfError = [&](Error E) {
68306c3fb27SDimitry Andric       auto [ErrorCode, Msg] = InstrProfError::take(std::move(E));
68406c3fb27SDimitry Andric       WC->Errors.emplace_back(make_error<InstrProfError>(ErrorCode, Msg),
68506c3fb27SDimitry Andric                               Filename);
68681ad6265SDimitry Andric     };
68781ad6265SDimitry Andric 
68881ad6265SDimitry Andric     // Add the frame mappings into the writer context.
68981ad6265SDimitry Andric     const auto &IdToFrame = Reader->getFrameMapping();
69081ad6265SDimitry Andric     for (const auto &I : IdToFrame) {
69181ad6265SDimitry Andric       bool Succeeded = WC->Writer.addMemProfFrame(
69281ad6265SDimitry Andric           /*Id=*/I.first, /*Frame=*/I.getSecond(), MemProfError);
69381ad6265SDimitry Andric       // If we weren't able to add the frame mappings then it doesn't make sense
69481ad6265SDimitry Andric       // to try to add the records from this profile.
69581ad6265SDimitry Andric       if (!Succeeded)
69681ad6265SDimitry Andric         return;
69781ad6265SDimitry Andric     }
698*0fca6ea1SDimitry Andric 
699*0fca6ea1SDimitry Andric     // Add the call stacks into the writer context.
700*0fca6ea1SDimitry Andric     const auto &CSIdToCallStacks = Reader->getCallStacks();
701*0fca6ea1SDimitry Andric     for (const auto &I : CSIdToCallStacks) {
702*0fca6ea1SDimitry Andric       bool Succeeded = WC->Writer.addMemProfCallStack(
703*0fca6ea1SDimitry Andric           /*Id=*/I.first, /*Frame=*/I.getSecond(), MemProfError);
704*0fca6ea1SDimitry Andric       // If we weren't able to add the call stacks then it doesn't make sense
705*0fca6ea1SDimitry Andric       // to try to add the records from this profile.
706*0fca6ea1SDimitry Andric       if (!Succeeded)
707*0fca6ea1SDimitry Andric         return;
708*0fca6ea1SDimitry Andric     }
709*0fca6ea1SDimitry Andric 
71081ad6265SDimitry Andric     const auto &FunctionProfileData = Reader->getProfileData();
71181ad6265SDimitry Andric     // Add the memprof records into the writer context.
712*0fca6ea1SDimitry Andric     for (const auto &[GUID, Record] : FunctionProfileData) {
713*0fca6ea1SDimitry Andric       WC->Writer.addMemProfRecord(GUID, Record);
71481ad6265SDimitry Andric     }
71581ad6265SDimitry Andric     return;
71681ad6265SDimitry Andric   }
71781ad6265SDimitry Andric 
71806c3fb27SDimitry Andric   auto FS = vfs::getRealFileSystem();
7195f757f3fSDimitry Andric   // TODO: This only saves the first non-fatal error from InstrProfReader, and
7205f757f3fSDimitry Andric   // then added to WriterContext::Errors. However, this is not extensible, if
7215f757f3fSDimitry Andric   // we have more non-fatal errors from InstrProfReader in the future. How
7225f757f3fSDimitry Andric   // should this interact with different -failure-mode?
7235f757f3fSDimitry Andric   std::optional<std::pair<Error, std::string>> ReaderWarning;
7245f757f3fSDimitry Andric   auto Warn = [&](Error E) {
7255f757f3fSDimitry Andric     if (ReaderWarning) {
7265f757f3fSDimitry Andric       consumeError(std::move(E));
7275f757f3fSDimitry Andric       return;
7285f757f3fSDimitry Andric     }
7295f757f3fSDimitry Andric     // Only show the first time an error occurs in this file.
7305f757f3fSDimitry Andric     auto [ErrCode, Msg] = InstrProfError::take(std::move(E));
7315f757f3fSDimitry Andric     ReaderWarning = {make_error<InstrProfError>(ErrCode, Msg), Filename};
7325f757f3fSDimitry Andric   };
7335f757f3fSDimitry Andric   auto ReaderOrErr =
7345f757f3fSDimitry Andric       InstrProfReader::create(Input.Filename, *FS, Correlator, Warn);
7350b57cec5SDimitry Andric   if (Error E = ReaderOrErr.takeError()) {
73606c3fb27SDimitry Andric     // Skip the empty profiles by returning silently.
73706c3fb27SDimitry Andric     auto [ErrCode, Msg] = InstrProfError::take(std::move(E));
73806c3fb27SDimitry Andric     if (ErrCode != instrprof_error::empty_raw_profile)
73906c3fb27SDimitry Andric       WC->Errors.emplace_back(make_error<InstrProfError>(ErrCode, Msg),
74006c3fb27SDimitry Andric                               Filename);
7410b57cec5SDimitry Andric     return;
7420b57cec5SDimitry Andric   }
7430b57cec5SDimitry Andric 
7440b57cec5SDimitry Andric   auto Reader = std::move(ReaderOrErr.get());
7451fd87a68SDimitry Andric   if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) {
746fe6060f1SDimitry Andric     consumeError(std::move(E));
7478bcb0991SDimitry Andric     WC->Errors.emplace_back(
7488bcb0991SDimitry Andric         make_error<StringError>(
7490b57cec5SDimitry Andric             "Merge IR generated profile with Clang generated profile.",
7508bcb0991SDimitry Andric             std::error_code()),
7518bcb0991SDimitry Andric         Filename);
7520b57cec5SDimitry Andric     return;
7530b57cec5SDimitry Andric   }
7540b57cec5SDimitry Andric 
7550b57cec5SDimitry Andric   for (auto &I : *Reader) {
7560b57cec5SDimitry Andric     if (Remapper)
7570b57cec5SDimitry Andric       I.Name = (*Remapper)(I.Name);
7580b57cec5SDimitry Andric     const StringRef FuncName = I.Name;
7590b57cec5SDimitry Andric     bool Reported = false;
7600b57cec5SDimitry Andric     WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) {
7610b57cec5SDimitry Andric       if (Reported) {
7620b57cec5SDimitry Andric         consumeError(std::move(E));
7630b57cec5SDimitry Andric         return;
7640b57cec5SDimitry Andric       }
7650b57cec5SDimitry Andric       Reported = true;
7660b57cec5SDimitry Andric       // Only show hint the first time an error occurs.
76706c3fb27SDimitry Andric       auto [ErrCode, Msg] = InstrProfError::take(std::move(E));
7680b57cec5SDimitry Andric       std::unique_lock<std::mutex> ErrGuard{WC->ErrLock};
76906c3fb27SDimitry Andric       bool firstTime = WC->WriterErrorCodes.insert(ErrCode).second;
77006c3fb27SDimitry Andric       handleMergeWriterError(make_error<InstrProfError>(ErrCode, Msg),
77106c3fb27SDimitry Andric                              Input.Filename, FuncName, firstTime);
7720b57cec5SDimitry Andric     });
7730b57cec5SDimitry Andric   }
774bdd1243dSDimitry Andric 
775*0fca6ea1SDimitry Andric   if (KeepVTableSymbols) {
776*0fca6ea1SDimitry Andric     const InstrProfSymtab &symtab = Reader->getSymtab();
777*0fca6ea1SDimitry Andric     const auto &VTableNames = symtab.getVTableNames();
778*0fca6ea1SDimitry Andric 
779*0fca6ea1SDimitry Andric     for (const auto &kv : VTableNames)
780*0fca6ea1SDimitry Andric       WC->Writer.addVTableName(kv.getKey());
781*0fca6ea1SDimitry Andric   }
782*0fca6ea1SDimitry Andric 
78306c3fb27SDimitry Andric   if (Reader->hasTemporalProfile()) {
78406c3fb27SDimitry Andric     auto &Traces = Reader->getTemporalProfTraces(Input.Weight);
78506c3fb27SDimitry Andric     if (!Traces.empty())
78606c3fb27SDimitry Andric       WC->Writer.addTemporalProfileTraces(
78706c3fb27SDimitry Andric           Traces, Reader->getTemporalProfTraceStreamSize());
78806c3fb27SDimitry Andric   }
789bdd1243dSDimitry Andric   if (Reader->hasError()) {
7905f757f3fSDimitry Andric     if (Error E = Reader->getError()) {
7918bcb0991SDimitry Andric       WC->Errors.emplace_back(std::move(E), Filename);
7925f757f3fSDimitry Andric       return;
7935f757f3fSDimitry Andric     }
7940b57cec5SDimitry Andric   }
7950b57cec5SDimitry Andric 
796bdd1243dSDimitry Andric   std::vector<llvm::object::BuildID> BinaryIds;
7975f757f3fSDimitry Andric   if (Error E = Reader->readBinaryIds(BinaryIds)) {
798bdd1243dSDimitry Andric     WC->Errors.emplace_back(std::move(E), Filename);
7995f757f3fSDimitry Andric     return;
8005f757f3fSDimitry Andric   }
801bdd1243dSDimitry Andric   WC->Writer.addBinaryIds(BinaryIds);
8025f757f3fSDimitry Andric 
8035f757f3fSDimitry Andric   if (ReaderWarning) {
8045f757f3fSDimitry Andric     WC->Errors.emplace_back(std::move(ReaderWarning->first),
8055f757f3fSDimitry Andric                             ReaderWarning->second);
8065f757f3fSDimitry Andric   }
807bdd1243dSDimitry Andric }
808bdd1243dSDimitry Andric 
8090b57cec5SDimitry Andric /// Merge the \p Src writer context into \p Dst.
mergeWriterContexts(WriterContext * Dst,WriterContext * Src)8100b57cec5SDimitry Andric static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) {
8118bcb0991SDimitry Andric   for (auto &ErrorPair : Src->Errors)
8128bcb0991SDimitry Andric     Dst->Errors.push_back(std::move(ErrorPair));
8138bcb0991SDimitry Andric   Src->Errors.clear();
8140b57cec5SDimitry Andric 
815bdd1243dSDimitry Andric   if (Error E = Dst->Writer.mergeProfileKind(Src->Writer.getProfileKind()))
816bdd1243dSDimitry Andric     exitWithError(std::move(E));
817bdd1243dSDimitry Andric 
8180b57cec5SDimitry Andric   Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) {
81906c3fb27SDimitry Andric     auto [ErrorCode, Msg] = InstrProfError::take(std::move(E));
8208bcb0991SDimitry Andric     std::unique_lock<std::mutex> ErrGuard{Dst->ErrLock};
82106c3fb27SDimitry Andric     bool firstTime = Dst->WriterErrorCodes.insert(ErrorCode).second;
8228bcb0991SDimitry Andric     if (firstTime)
82306c3fb27SDimitry Andric       warn(toString(make_error<InstrProfError>(ErrorCode, Msg)));
8240b57cec5SDimitry Andric   });
8250b57cec5SDimitry Andric }
8260b57cec5SDimitry Andric 
8277a6dacacSDimitry Andric static StringRef
getFuncName(const StringMap<InstrProfWriter::ProfilingData>::value_type & Val)8287a6dacacSDimitry Andric getFuncName(const StringMap<InstrProfWriter::ProfilingData>::value_type &Val) {
8297a6dacacSDimitry Andric   return Val.first();
8307a6dacacSDimitry Andric }
8317a6dacacSDimitry Andric 
8327a6dacacSDimitry Andric static std::string
getFuncName(const SampleProfileMap::value_type & Val)8337a6dacacSDimitry Andric getFuncName(const SampleProfileMap::value_type &Val) {
8347a6dacacSDimitry Andric   return Val.second.getContext().toString();
8357a6dacacSDimitry Andric }
8367a6dacacSDimitry Andric 
8377a6dacacSDimitry Andric template <typename T>
filterFunctions(T & ProfileMap)8387a6dacacSDimitry Andric static void filterFunctions(T &ProfileMap) {
8397a6dacacSDimitry Andric   bool hasFilter = !FuncNameFilter.empty();
8407a6dacacSDimitry Andric   bool hasNegativeFilter = !FuncNameNegativeFilter.empty();
8417a6dacacSDimitry Andric   if (!hasFilter && !hasNegativeFilter)
8427a6dacacSDimitry Andric     return;
8437a6dacacSDimitry Andric 
8447a6dacacSDimitry Andric   // If filter starts with '?' it is MSVC mangled name, not a regex.
8457a6dacacSDimitry Andric   llvm::Regex ProbablyMSVCMangledName("[?@$_0-9A-Za-z]+");
8467a6dacacSDimitry Andric   if (hasFilter && FuncNameFilter[0] == '?' &&
8477a6dacacSDimitry Andric       ProbablyMSVCMangledName.match(FuncNameFilter))
8487a6dacacSDimitry Andric     FuncNameFilter = llvm::Regex::escape(FuncNameFilter);
8497a6dacacSDimitry Andric   if (hasNegativeFilter && FuncNameNegativeFilter[0] == '?' &&
8507a6dacacSDimitry Andric       ProbablyMSVCMangledName.match(FuncNameNegativeFilter))
8517a6dacacSDimitry Andric     FuncNameNegativeFilter = llvm::Regex::escape(FuncNameNegativeFilter);
8527a6dacacSDimitry Andric 
8537a6dacacSDimitry Andric   size_t Count = ProfileMap.size();
8547a6dacacSDimitry Andric   llvm::Regex Pattern(FuncNameFilter);
8557a6dacacSDimitry Andric   llvm::Regex NegativePattern(FuncNameNegativeFilter);
8567a6dacacSDimitry Andric   std::string Error;
8577a6dacacSDimitry Andric   if (hasFilter && !Pattern.isValid(Error))
8587a6dacacSDimitry Andric     exitWithError(Error);
8597a6dacacSDimitry Andric   if (hasNegativeFilter && !NegativePattern.isValid(Error))
8607a6dacacSDimitry Andric     exitWithError(Error);
8617a6dacacSDimitry Andric 
8627a6dacacSDimitry Andric   // Handle MD5 profile, so it is still able to match using the original name.
8637a6dacacSDimitry Andric   std::string MD5Name = std::to_string(llvm::MD5Hash(FuncNameFilter));
8647a6dacacSDimitry Andric   std::string NegativeMD5Name =
8657a6dacacSDimitry Andric       std::to_string(llvm::MD5Hash(FuncNameNegativeFilter));
8667a6dacacSDimitry Andric 
8677a6dacacSDimitry Andric   for (auto I = ProfileMap.begin(); I != ProfileMap.end();) {
8687a6dacacSDimitry Andric     auto Tmp = I++;
8697a6dacacSDimitry Andric     const auto &FuncName = getFuncName(*Tmp);
8707a6dacacSDimitry Andric     // Negative filter has higher precedence than positive filter.
8717a6dacacSDimitry Andric     if ((hasNegativeFilter &&
8727a6dacacSDimitry Andric          (NegativePattern.match(FuncName) ||
8737a6dacacSDimitry Andric           (FunctionSamples::UseMD5 && NegativeMD5Name == FuncName))) ||
8747a6dacacSDimitry Andric         (hasFilter && !(Pattern.match(FuncName) ||
8757a6dacacSDimitry Andric                         (FunctionSamples::UseMD5 && MD5Name == FuncName))))
8767a6dacacSDimitry Andric       ProfileMap.erase(Tmp);
8777a6dacacSDimitry Andric   }
8787a6dacacSDimitry Andric 
8797a6dacacSDimitry Andric   llvm::dbgs() << Count - ProfileMap.size() << " of " << Count << " functions "
8807a6dacacSDimitry Andric                << "in the original profile are filtered.\n";
8817a6dacacSDimitry Andric }
8827a6dacacSDimitry Andric 
writeInstrProfile(StringRef OutputFilename,ProfileFormat OutputFormat,InstrProfWriter & Writer)8835ffd83dbSDimitry Andric static void writeInstrProfile(StringRef OutputFilename,
8845ffd83dbSDimitry Andric                               ProfileFormat OutputFormat,
8855ffd83dbSDimitry Andric                               InstrProfWriter &Writer) {
8865ffd83dbSDimitry Andric   std::error_code EC;
887e8d8bef9SDimitry Andric   raw_fd_ostream Output(OutputFilename.data(), EC,
888fe6060f1SDimitry Andric                         OutputFormat == PF_Text ? sys::fs::OF_TextWithCRLF
889e8d8bef9SDimitry Andric                                                 : sys::fs::OF_None);
8905ffd83dbSDimitry Andric   if (EC)
8915ffd83dbSDimitry Andric     exitWithErrorCode(EC, OutputFilename);
8925ffd83dbSDimitry Andric 
8935ffd83dbSDimitry Andric   if (OutputFormat == PF_Text) {
8945ffd83dbSDimitry Andric     if (Error E = Writer.writeText(Output))
895fe6060f1SDimitry Andric       warn(std::move(E));
8965ffd83dbSDimitry Andric   } else {
897fe6060f1SDimitry Andric     if (Output.is_displayed())
898fe6060f1SDimitry Andric       exitWithError("cannot write a non-text format profile to the terminal");
899fe6060f1SDimitry Andric     if (Error E = Writer.write(Output))
900fe6060f1SDimitry Andric       warn(std::move(E));
9015ffd83dbSDimitry Andric   }
9025ffd83dbSDimitry Andric }
9035ffd83dbSDimitry Andric 
mergeInstrProfile(const WeightedFileVector & Inputs,SymbolRemapper * Remapper,int MaxDbgCorrelationWarnings,const StringRef ProfiledBinary)9045f757f3fSDimitry Andric static void mergeInstrProfile(const WeightedFileVector &Inputs,
9055f757f3fSDimitry Andric                               SymbolRemapper *Remapper,
9065f757f3fSDimitry Andric                               int MaxDbgCorrelationWarnings,
90781ad6265SDimitry Andric                               const StringRef ProfiledBinary) {
9085f757f3fSDimitry Andric   const uint64_t TraceReservoirSize = TemporalProfTraceReservoirSize.getValue();
9095f757f3fSDimitry Andric   const uint64_t MaxTraceLength = TemporalProfMaxTraceLength.getValue();
91006c3fb27SDimitry Andric   if (OutputFormat == PF_Compact_Binary)
91106c3fb27SDimitry Andric     exitWithError("Compact Binary is deprecated");
91206c3fb27SDimitry Andric   if (OutputFormat != PF_Binary && OutputFormat != PF_Ext_Binary &&
91306c3fb27SDimitry Andric       OutputFormat != PF_Text)
914fe6060f1SDimitry Andric     exitWithError("unknown format is specified");
9150b57cec5SDimitry Andric 
9165f757f3fSDimitry Andric   // TODO: Maybe we should support correlation with mixture of different
9175f757f3fSDimitry Andric   // correlation modes(w/wo debug-info/object correlation).
9185f757f3fSDimitry Andric   if (!DebugInfoFilename.empty() && !BinaryFilename.empty())
9195f757f3fSDimitry Andric     exitWithError("Expected only one of -debug-info, -binary-file");
9205f757f3fSDimitry Andric   std::string CorrelateFilename;
9215f757f3fSDimitry Andric   ProfCorrelatorKind CorrelateKind = ProfCorrelatorKind::NONE;
9220eae32dcSDimitry Andric   if (!DebugInfoFilename.empty()) {
9235f757f3fSDimitry Andric     CorrelateFilename = DebugInfoFilename;
9245f757f3fSDimitry Andric     CorrelateKind = ProfCorrelatorKind::DEBUG_INFO;
9255f757f3fSDimitry Andric   } else if (!BinaryFilename.empty()) {
9265f757f3fSDimitry Andric     CorrelateFilename = BinaryFilename;
9275f757f3fSDimitry Andric     CorrelateKind = ProfCorrelatorKind::BINARY;
9285f757f3fSDimitry Andric   }
9295f757f3fSDimitry Andric 
9305f757f3fSDimitry Andric   std::unique_ptr<InstrProfCorrelator> Correlator;
9315f757f3fSDimitry Andric   if (CorrelateKind != InstrProfCorrelator::NONE) {
9325f757f3fSDimitry Andric     if (auto Err = InstrProfCorrelator::get(CorrelateFilename, CorrelateKind)
9335f757f3fSDimitry Andric                        .moveInto(Correlator))
9345f757f3fSDimitry Andric       exitWithError(std::move(Err), CorrelateFilename);
9355f757f3fSDimitry Andric     if (auto Err = Correlator->correlateProfileData(MaxDbgCorrelationWarnings))
9365f757f3fSDimitry Andric       exitWithError(std::move(Err), CorrelateFilename);
9370eae32dcSDimitry Andric   }
9380eae32dcSDimitry Andric 
9390b57cec5SDimitry Andric   std::mutex ErrorLock;
9400b57cec5SDimitry Andric   SmallSet<instrprof_error, 4> WriterErrorCodes;
9410b57cec5SDimitry Andric 
9420b57cec5SDimitry Andric   // If NumThreads is not specified, auto-detect a good default.
9430b57cec5SDimitry Andric   if (NumThreads == 0)
9445ffd83dbSDimitry Andric     NumThreads = std::min(hardware_concurrency().compute_thread_count(),
9455ffd83dbSDimitry Andric                           unsigned((Inputs.size() + 1) / 2));
9460b57cec5SDimitry Andric 
9470b57cec5SDimitry Andric   // Initialize the writer contexts.
9480b57cec5SDimitry Andric   SmallVector<std::unique_ptr<WriterContext>, 4> Contexts;
9490b57cec5SDimitry Andric   for (unsigned I = 0; I < NumThreads; ++I)
9508bcb0991SDimitry Andric     Contexts.emplace_back(std::make_unique<WriterContext>(
95106c3fb27SDimitry Andric         OutputSparse, ErrorLock, WriterErrorCodes, TraceReservoirSize,
95206c3fb27SDimitry Andric         MaxTraceLength));
9530b57cec5SDimitry Andric 
9540b57cec5SDimitry Andric   if (NumThreads == 1) {
9550b57cec5SDimitry Andric     for (const auto &Input : Inputs)
95681ad6265SDimitry Andric       loadInput(Input, Remapper, Correlator.get(), ProfiledBinary,
95781ad6265SDimitry Andric                 Contexts[0].get());
9580b57cec5SDimitry Andric   } else {
959*0fca6ea1SDimitry Andric     DefaultThreadPool Pool(hardware_concurrency(NumThreads));
9600b57cec5SDimitry Andric 
9610b57cec5SDimitry Andric     // Load the inputs in parallel (N/NumThreads serial steps).
9620b57cec5SDimitry Andric     unsigned Ctx = 0;
9630b57cec5SDimitry Andric     for (const auto &Input : Inputs) {
96481ad6265SDimitry Andric       Pool.async(loadInput, Input, Remapper, Correlator.get(), ProfiledBinary,
9650eae32dcSDimitry Andric                  Contexts[Ctx].get());
9660b57cec5SDimitry Andric       Ctx = (Ctx + 1) % NumThreads;
9670b57cec5SDimitry Andric     }
9680b57cec5SDimitry Andric     Pool.wait();
9690b57cec5SDimitry Andric 
9700b57cec5SDimitry Andric     // Merge the writer contexts together (~ lg(NumThreads) serial steps).
9710b57cec5SDimitry Andric     unsigned Mid = Contexts.size() / 2;
9720b57cec5SDimitry Andric     unsigned End = Contexts.size();
9730b57cec5SDimitry Andric     assert(Mid > 0 && "Expected more than one context");
9740b57cec5SDimitry Andric     do {
9750b57cec5SDimitry Andric       for (unsigned I = 0; I < Mid; ++I)
9760b57cec5SDimitry Andric         Pool.async(mergeWriterContexts, Contexts[I].get(),
9770b57cec5SDimitry Andric                    Contexts[I + Mid].get());
9780b57cec5SDimitry Andric       Pool.wait();
9790b57cec5SDimitry Andric       if (End & 1) {
9800b57cec5SDimitry Andric         Pool.async(mergeWriterContexts, Contexts[0].get(),
9810b57cec5SDimitry Andric                    Contexts[End - 1].get());
9820b57cec5SDimitry Andric         Pool.wait();
9830b57cec5SDimitry Andric       }
9840b57cec5SDimitry Andric       End = Mid;
9850b57cec5SDimitry Andric       Mid /= 2;
9860b57cec5SDimitry Andric     } while (Mid > 0);
9870b57cec5SDimitry Andric   }
9880b57cec5SDimitry Andric 
9898bcb0991SDimitry Andric   // Handle deferred errors encountered during merging. If the number of errors
9908bcb0991SDimitry Andric   // is equal to the number of inputs the merge failed.
9918bcb0991SDimitry Andric   unsigned NumErrors = 0;
9920b57cec5SDimitry Andric   for (std::unique_ptr<WriterContext> &WC : Contexts) {
9938bcb0991SDimitry Andric     for (auto &ErrorPair : WC->Errors) {
9948bcb0991SDimitry Andric       ++NumErrors;
9958bcb0991SDimitry Andric       warn(toString(std::move(ErrorPair.first)), ErrorPair.second);
9960b57cec5SDimitry Andric     }
9978bcb0991SDimitry Andric   }
9985f757f3fSDimitry Andric   if ((NumErrors == Inputs.size() && FailMode == failIfAllAreInvalid) ||
9998bcb0991SDimitry Andric       (NumErrors > 0 && FailMode == failIfAnyAreInvalid))
1000fe6060f1SDimitry Andric     exitWithError("no profile can be merged");
10010b57cec5SDimitry Andric 
10027a6dacacSDimitry Andric   filterFunctions(Contexts[0]->Writer.getProfileData());
10037a6dacacSDimitry Andric 
10045ffd83dbSDimitry Andric   writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer);
10050b57cec5SDimitry Andric }
10060b57cec5SDimitry Andric 
1007e8d8bef9SDimitry Andric /// The profile entry for a function in instrumentation profile.
1008e8d8bef9SDimitry Andric struct InstrProfileEntry {
1009e8d8bef9SDimitry Andric   uint64_t MaxCount = 0;
1010bdd1243dSDimitry Andric   uint64_t NumEdgeCounters = 0;
1011e8d8bef9SDimitry Andric   float ZeroCounterRatio = 0.0;
1012e8d8bef9SDimitry Andric   InstrProfRecord *ProfRecord;
1013e8d8bef9SDimitry Andric   InstrProfileEntry(InstrProfRecord *Record);
1014e8d8bef9SDimitry Andric   InstrProfileEntry() = default;
1015e8d8bef9SDimitry Andric };
1016e8d8bef9SDimitry Andric 
InstrProfileEntry(InstrProfRecord * Record)1017e8d8bef9SDimitry Andric InstrProfileEntry::InstrProfileEntry(InstrProfRecord *Record) {
1018e8d8bef9SDimitry Andric   ProfRecord = Record;
1019e8d8bef9SDimitry Andric   uint64_t CntNum = Record->Counts.size();
1020e8d8bef9SDimitry Andric   uint64_t ZeroCntNum = 0;
1021e8d8bef9SDimitry Andric   for (size_t I = 0; I < CntNum; ++I) {
1022e8d8bef9SDimitry Andric     MaxCount = std::max(MaxCount, Record->Counts[I]);
1023e8d8bef9SDimitry Andric     ZeroCntNum += !Record->Counts[I];
1024e8d8bef9SDimitry Andric   }
1025e8d8bef9SDimitry Andric   ZeroCounterRatio = (float)ZeroCntNum / CntNum;
1026bdd1243dSDimitry Andric   NumEdgeCounters = CntNum;
1027e8d8bef9SDimitry Andric }
1028e8d8bef9SDimitry Andric 
1029bdd1243dSDimitry Andric /// Either set all the counters in the instr profile entry \p IFE to
1030bdd1243dSDimitry Andric /// -1 / -2 /in order to drop the profile or scale up the
1031bdd1243dSDimitry Andric /// counters in \p IFP to be above hot / cold threshold. We use
1032bdd1243dSDimitry Andric /// the ratio of zero counters in the profile of a function to
1033bdd1243dSDimitry Andric /// decide the profile is helpful or harmful for performance,
1034bdd1243dSDimitry Andric /// and to choose whether to scale up or drop it.
updateInstrProfileEntry(InstrProfileEntry & IFE,bool SetToHot,uint64_t HotInstrThreshold,uint64_t ColdInstrThreshold,float ZeroCounterThreshold)1035bdd1243dSDimitry Andric static void updateInstrProfileEntry(InstrProfileEntry &IFE, bool SetToHot,
1036e8d8bef9SDimitry Andric                                     uint64_t HotInstrThreshold,
1037bdd1243dSDimitry Andric                                     uint64_t ColdInstrThreshold,
1038e8d8bef9SDimitry Andric                                     float ZeroCounterThreshold) {
1039e8d8bef9SDimitry Andric   InstrProfRecord *ProfRecord = IFE.ProfRecord;
1040e8d8bef9SDimitry Andric   if (!IFE.MaxCount || IFE.ZeroCounterRatio > ZeroCounterThreshold) {
1041e8d8bef9SDimitry Andric     // If all or most of the counters of the function are zero, the
1042bdd1243dSDimitry Andric     // profile is unaccountable and should be dropped. Reset all the
1043bdd1243dSDimitry Andric     // counters to be -1 / -2 and PGO profile-use will drop the profile.
1044e8d8bef9SDimitry Andric     // All counters being -1 also implies that the function is hot so
1045e8d8bef9SDimitry Andric     // PGO profile-use will also set the entry count metadata to be
1046e8d8bef9SDimitry Andric     // above hot threshold.
1047bdd1243dSDimitry Andric     // All counters being -2 implies that the function is warm so
1048bdd1243dSDimitry Andric     // PGO profile-use will also set the entry count metadata to be
1049bdd1243dSDimitry Andric     // above cold threshold.
1050bdd1243dSDimitry Andric     auto Kind =
1051bdd1243dSDimitry Andric         (SetToHot ? InstrProfRecord::PseudoHot : InstrProfRecord::PseudoWarm);
1052bdd1243dSDimitry Andric     ProfRecord->setPseudoCount(Kind);
1053e8d8bef9SDimitry Andric     return;
1054e8d8bef9SDimitry Andric   }
1055e8d8bef9SDimitry Andric 
1056bdd1243dSDimitry Andric   // Scale up the MaxCount to be multiple times above hot / cold threshold.
1057e8d8bef9SDimitry Andric   const unsigned MultiplyFactor = 3;
1058bdd1243dSDimitry Andric   uint64_t Threshold = (SetToHot ? HotInstrThreshold : ColdInstrThreshold);
1059bdd1243dSDimitry Andric   uint64_t Numerator = Threshold * MultiplyFactor;
1060bdd1243dSDimitry Andric 
1061bdd1243dSDimitry Andric   // Make sure Threshold for warm counters is below the HotInstrThreshold.
1062bdd1243dSDimitry Andric   if (!SetToHot && Threshold >= HotInstrThreshold) {
1063bdd1243dSDimitry Andric     Threshold = (HotInstrThreshold + ColdInstrThreshold) / 2;
1064bdd1243dSDimitry Andric   }
1065bdd1243dSDimitry Andric 
1066e8d8bef9SDimitry Andric   uint64_t Denominator = IFE.MaxCount;
1067bdd1243dSDimitry Andric   if (Numerator <= Denominator)
1068bdd1243dSDimitry Andric     return;
1069e8d8bef9SDimitry Andric   ProfRecord->scale(Numerator, Denominator, [&](instrprof_error E) {
1070e8d8bef9SDimitry Andric     warn(toString(make_error<InstrProfError>(E)));
1071e8d8bef9SDimitry Andric   });
1072e8d8bef9SDimitry Andric }
1073e8d8bef9SDimitry Andric 
1074e8d8bef9SDimitry Andric const uint64_t ColdPercentileIdx = 15;
1075e8d8bef9SDimitry Andric const uint64_t HotPercentileIdx = 11;
1076e8d8bef9SDimitry Andric 
1077fe6060f1SDimitry Andric using sampleprof::FSDiscriminatorPass;
1078fe6060f1SDimitry Andric 
1079fe6060f1SDimitry Andric // Internal options to set FSDiscriminatorPass. Used in merge and show
1080fe6060f1SDimitry Andric // commands.
1081fe6060f1SDimitry Andric static cl::opt<FSDiscriminatorPass> FSDiscriminatorPassOption(
1082fe6060f1SDimitry Andric     "fs-discriminator-pass", cl::init(PassLast), cl::Hidden,
1083fe6060f1SDimitry Andric     cl::desc("Zero out the discriminator bits for the FS discrimiantor "
1084fe6060f1SDimitry Andric              "pass beyond this value. The enum values are defined in "
1085fe6060f1SDimitry Andric              "Support/Discriminator.h"),
1086fe6060f1SDimitry Andric     cl::values(clEnumVal(Base, "Use base discriminators only"),
1087fe6060f1SDimitry Andric                clEnumVal(Pass1, "Use base and pass 1 discriminators"),
1088fe6060f1SDimitry Andric                clEnumVal(Pass2, "Use base and pass 1-2 discriminators"),
1089fe6060f1SDimitry Andric                clEnumVal(Pass3, "Use base and pass 1-3 discriminators"),
1090fe6060f1SDimitry Andric                clEnumVal(PassLast, "Use all discriminator bits (default)")));
1091fe6060f1SDimitry Andric 
getDiscriminatorMask()1092fe6060f1SDimitry Andric static unsigned getDiscriminatorMask() {
1093fe6060f1SDimitry Andric   return getN1Bits(getFSPassBitEnd(FSDiscriminatorPassOption.getValue()));
1094fe6060f1SDimitry Andric }
1095fe6060f1SDimitry Andric 
1096e8d8bef9SDimitry Andric /// Adjust the instr profile in \p WC based on the sample profile in
1097e8d8bef9SDimitry Andric /// \p Reader.
1098e8d8bef9SDimitry Andric static void
adjustInstrProfile(std::unique_ptr<WriterContext> & WC,std::unique_ptr<sampleprof::SampleProfileReader> & Reader,unsigned SupplMinSizeThreshold,float ZeroCounterThreshold,unsigned InstrProfColdThreshold)1099e8d8bef9SDimitry Andric adjustInstrProfile(std::unique_ptr<WriterContext> &WC,
1100e8d8bef9SDimitry Andric                    std::unique_ptr<sampleprof::SampleProfileReader> &Reader,
1101e8d8bef9SDimitry Andric                    unsigned SupplMinSizeThreshold, float ZeroCounterThreshold,
1102e8d8bef9SDimitry Andric                    unsigned InstrProfColdThreshold) {
1103e8d8bef9SDimitry Andric   // Function to its entry in instr profile.
1104e8d8bef9SDimitry Andric   StringMap<InstrProfileEntry> InstrProfileMap;
1105bdd1243dSDimitry Andric   StringMap<StringRef> StaticFuncMap;
1106e8d8bef9SDimitry Andric   InstrProfSummaryBuilder IPBuilder(ProfileSummaryBuilder::DefaultCutoffs);
1107bdd1243dSDimitry Andric 
1108bdd1243dSDimitry Andric   auto checkSampleProfileHasFUnique = [&Reader]() {
1109bdd1243dSDimitry Andric     for (const auto &PD : Reader->getProfiles()) {
11105f757f3fSDimitry Andric       auto &FContext = PD.second.getContext();
1111bdd1243dSDimitry Andric       if (FContext.toString().find(FunctionSamples::UniqSuffix) !=
1112bdd1243dSDimitry Andric           std::string::npos) {
1113bdd1243dSDimitry Andric         return true;
1114bdd1243dSDimitry Andric       }
1115bdd1243dSDimitry Andric     }
1116bdd1243dSDimitry Andric     return false;
1117bdd1243dSDimitry Andric   };
1118bdd1243dSDimitry Andric 
1119bdd1243dSDimitry Andric   bool SampleProfileHasFUnique = checkSampleProfileHasFUnique();
1120bdd1243dSDimitry Andric 
1121bdd1243dSDimitry Andric   auto buildStaticFuncMap = [&StaticFuncMap,
1122bdd1243dSDimitry Andric                              SampleProfileHasFUnique](const StringRef Name) {
11231db9f3b2SDimitry Andric     std::string FilePrefixes[] = {".cpp", "cc", ".c", ".hpp", ".h"};
1124bdd1243dSDimitry Andric     size_t PrefixPos = StringRef::npos;
11251db9f3b2SDimitry Andric     for (auto &FilePrefix : FilePrefixes) {
1126*0fca6ea1SDimitry Andric       std::string NamePrefix = FilePrefix + GlobalIdentifierDelimiter;
11271db9f3b2SDimitry Andric       PrefixPos = Name.find_insensitive(NamePrefix);
1128bdd1243dSDimitry Andric       if (PrefixPos == StringRef::npos)
1129bdd1243dSDimitry Andric         continue;
11301db9f3b2SDimitry Andric       PrefixPos += NamePrefix.size();
1131bdd1243dSDimitry Andric       break;
1132bdd1243dSDimitry Andric     }
1133bdd1243dSDimitry Andric 
1134bdd1243dSDimitry Andric     if (PrefixPos == StringRef::npos) {
1135bdd1243dSDimitry Andric       return;
1136bdd1243dSDimitry Andric     }
1137bdd1243dSDimitry Andric 
1138bdd1243dSDimitry Andric     StringRef NewName = Name.drop_front(PrefixPos);
1139bdd1243dSDimitry Andric     StringRef FName = Name.substr(0, PrefixPos - 1);
1140bdd1243dSDimitry Andric     if (NewName.size() == 0) {
1141bdd1243dSDimitry Andric       return;
1142bdd1243dSDimitry Andric     }
1143bdd1243dSDimitry Andric 
1144bdd1243dSDimitry Andric     // This name should have a static linkage.
1145bdd1243dSDimitry Andric     size_t PostfixPos = NewName.find(FunctionSamples::UniqSuffix);
1146bdd1243dSDimitry Andric     bool ProfileHasFUnique = (PostfixPos != StringRef::npos);
1147bdd1243dSDimitry Andric 
1148bdd1243dSDimitry Andric     // If sample profile and instrumented profile do not agree on symbol
1149bdd1243dSDimitry Andric     // uniqification.
1150bdd1243dSDimitry Andric     if (SampleProfileHasFUnique != ProfileHasFUnique) {
11515f757f3fSDimitry Andric       // If instrumented profile uses -funique-internal-linkage-symbols,
1152bdd1243dSDimitry Andric       // we need to trim the name.
1153bdd1243dSDimitry Andric       if (ProfileHasFUnique) {
1154bdd1243dSDimitry Andric         NewName = NewName.substr(0, PostfixPos);
1155bdd1243dSDimitry Andric       } else {
11565f757f3fSDimitry Andric         // If sample profile uses -funique-internal-linkage-symbols,
1157bdd1243dSDimitry Andric         // we build the map.
1158bdd1243dSDimitry Andric         std::string NStr =
1159bdd1243dSDimitry Andric             NewName.str() + getUniqueInternalLinkagePostfix(FName);
1160bdd1243dSDimitry Andric         NewName = StringRef(NStr);
1161bdd1243dSDimitry Andric         StaticFuncMap[NewName] = Name;
1162bdd1243dSDimitry Andric         return;
1163bdd1243dSDimitry Andric       }
1164bdd1243dSDimitry Andric     }
1165bdd1243dSDimitry Andric 
116606c3fb27SDimitry Andric     if (!StaticFuncMap.contains(NewName)) {
1167bdd1243dSDimitry Andric       StaticFuncMap[NewName] = Name;
1168bdd1243dSDimitry Andric     } else {
1169bdd1243dSDimitry Andric       StaticFuncMap[NewName] = DuplicateNameStr;
1170bdd1243dSDimitry Andric     }
1171bdd1243dSDimitry Andric   };
1172bdd1243dSDimitry Andric 
1173bdd1243dSDimitry Andric   // We need to flatten the SampleFDO profile as the InstrFDO
1174bdd1243dSDimitry Andric   // profile does not have inlined callsite profiles.
1175bdd1243dSDimitry Andric   // One caveat is the pre-inlined function -- their samples
1176bdd1243dSDimitry Andric   // should be collapsed into the caller function.
1177bdd1243dSDimitry Andric   // Here we do a DFS traversal to get the flatten profile
1178bdd1243dSDimitry Andric   // info: the sum of entrycount and the max of maxcount.
1179bdd1243dSDimitry Andric   // Here is the algorithm:
1180bdd1243dSDimitry Andric   //   recursive (FS, root_name) {
1181bdd1243dSDimitry Andric   //      name = FS->getName();
1182bdd1243dSDimitry Andric   //      get samples for FS;
1183bdd1243dSDimitry Andric   //      if (InstrProf.find(name) {
1184bdd1243dSDimitry Andric   //        root_name = name;
1185bdd1243dSDimitry Andric   //      } else {
1186bdd1243dSDimitry Andric   //        if (name is in static_func map) {
1187bdd1243dSDimitry Andric   //          root_name = static_name;
1188bdd1243dSDimitry Andric   //        }
1189bdd1243dSDimitry Andric   //      }
1190bdd1243dSDimitry Andric   //      update the Map entry for root_name;
1191bdd1243dSDimitry Andric   //      for (subfs: FS) {
1192bdd1243dSDimitry Andric   //        recursive(subfs, root_name);
1193bdd1243dSDimitry Andric   //      }
1194bdd1243dSDimitry Andric   //   }
1195bdd1243dSDimitry Andric   //
1196bdd1243dSDimitry Andric   // Here is an example.
1197bdd1243dSDimitry Andric   //
1198bdd1243dSDimitry Andric   // SampleProfile:
1199bdd1243dSDimitry Andric   // foo:12345:1000
1200bdd1243dSDimitry Andric   // 1: 1000
1201bdd1243dSDimitry Andric   // 2.1: 1000
1202bdd1243dSDimitry Andric   // 15: 5000
1203bdd1243dSDimitry Andric   // 4: bar:1000
1204bdd1243dSDimitry Andric   //  1: 1000
1205bdd1243dSDimitry Andric   //  2: goo:3000
1206bdd1243dSDimitry Andric   //   1: 3000
1207bdd1243dSDimitry Andric   // 8: bar:40000
1208bdd1243dSDimitry Andric   //  1: 10000
1209bdd1243dSDimitry Andric   //  2: goo:30000
1210bdd1243dSDimitry Andric   //   1: 30000
1211bdd1243dSDimitry Andric   //
1212bdd1243dSDimitry Andric   // InstrProfile has two entries:
1213bdd1243dSDimitry Andric   //  foo
12141db9f3b2SDimitry Andric   //  bar.cc;bar
1215bdd1243dSDimitry Andric   //
1216bdd1243dSDimitry Andric   // After BuildMaxSampleMap, we should have the following in FlattenSampleMap:
1217bdd1243dSDimitry Andric   // {"foo", {1000, 5000}}
12181db9f3b2SDimitry Andric   // {"bar.cc;bar", {11000, 30000}}
1219bdd1243dSDimitry Andric   //
1220bdd1243dSDimitry Andric   // foo's has an entry count of 1000, and max body count of 5000.
12211db9f3b2SDimitry Andric   // bar.cc;bar has an entry count of 11000 (sum two callsites of 1000 and
1222bdd1243dSDimitry Andric   // 10000), and max count of 30000 (from the callsite in line 8).
1223bdd1243dSDimitry Andric   //
12241db9f3b2SDimitry Andric   // Note that goo's count will remain in bar.cc;bar() as it does not have an
1225bdd1243dSDimitry Andric   // entry in InstrProfile.
12265f757f3fSDimitry Andric   llvm::StringMap<std::pair<uint64_t, uint64_t>> FlattenSampleMap;
1227bdd1243dSDimitry Andric   auto BuildMaxSampleMap = [&FlattenSampleMap, &StaticFuncMap,
1228bdd1243dSDimitry Andric                             &InstrProfileMap](const FunctionSamples &FS,
1229bdd1243dSDimitry Andric                                               const StringRef &RootName) {
1230bdd1243dSDimitry Andric     auto BuildMaxSampleMapImpl = [&](const FunctionSamples &FS,
1231bdd1243dSDimitry Andric                                      const StringRef &RootName,
1232bdd1243dSDimitry Andric                                      auto &BuildImpl) -> void {
12335f757f3fSDimitry Andric       std::string NameStr = FS.getFunction().str();
12345f757f3fSDimitry Andric       const StringRef Name = NameStr;
1235bdd1243dSDimitry Andric       const StringRef *NewRootName = &RootName;
1236bdd1243dSDimitry Andric       uint64_t EntrySample = FS.getHeadSamplesEstimate();
1237bdd1243dSDimitry Andric       uint64_t MaxBodySample = FS.getMaxCountInside(/* SkipCallSite*/ true);
1238bdd1243dSDimitry Andric 
1239bdd1243dSDimitry Andric       auto It = InstrProfileMap.find(Name);
1240bdd1243dSDimitry Andric       if (It != InstrProfileMap.end()) {
1241bdd1243dSDimitry Andric         NewRootName = &Name;
1242bdd1243dSDimitry Andric       } else {
1243bdd1243dSDimitry Andric         auto NewName = StaticFuncMap.find(Name);
1244bdd1243dSDimitry Andric         if (NewName != StaticFuncMap.end()) {
1245bdd1243dSDimitry Andric           It = InstrProfileMap.find(NewName->second.str());
1246bdd1243dSDimitry Andric           if (NewName->second != DuplicateNameStr) {
1247bdd1243dSDimitry Andric             NewRootName = &NewName->second;
1248bdd1243dSDimitry Andric           }
1249bdd1243dSDimitry Andric         } else {
1250bdd1243dSDimitry Andric           // Here the EntrySample is of an inlined function, so we should not
1251bdd1243dSDimitry Andric           // update the EntrySample in the map.
1252bdd1243dSDimitry Andric           EntrySample = 0;
1253bdd1243dSDimitry Andric         }
1254bdd1243dSDimitry Andric       }
1255bdd1243dSDimitry Andric       EntrySample += FlattenSampleMap[*NewRootName].first;
1256bdd1243dSDimitry Andric       MaxBodySample =
1257bdd1243dSDimitry Andric           std::max(FlattenSampleMap[*NewRootName].second, MaxBodySample);
1258bdd1243dSDimitry Andric       FlattenSampleMap[*NewRootName] =
1259bdd1243dSDimitry Andric           std::make_pair(EntrySample, MaxBodySample);
1260bdd1243dSDimitry Andric 
1261bdd1243dSDimitry Andric       for (const auto &C : FS.getCallsiteSamples())
1262bdd1243dSDimitry Andric         for (const auto &F : C.second)
1263bdd1243dSDimitry Andric           BuildImpl(F.second, *NewRootName, BuildImpl);
1264bdd1243dSDimitry Andric     };
1265bdd1243dSDimitry Andric     BuildMaxSampleMapImpl(FS, RootName, BuildMaxSampleMapImpl);
1266bdd1243dSDimitry Andric   };
1267bdd1243dSDimitry Andric 
1268e8d8bef9SDimitry Andric   for (auto &PD : WC->Writer.getProfileData()) {
1269e8d8bef9SDimitry Andric     // Populate IPBuilder.
1270e8d8bef9SDimitry Andric     for (const auto &PDV : PD.getValue()) {
1271e8d8bef9SDimitry Andric       InstrProfRecord Record = PDV.second;
1272e8d8bef9SDimitry Andric       IPBuilder.addRecord(Record);
1273e8d8bef9SDimitry Andric     }
1274e8d8bef9SDimitry Andric 
1275e8d8bef9SDimitry Andric     // If a function has multiple entries in instr profile, skip it.
1276e8d8bef9SDimitry Andric     if (PD.getValue().size() != 1)
1277e8d8bef9SDimitry Andric       continue;
1278e8d8bef9SDimitry Andric 
1279e8d8bef9SDimitry Andric     // Initialize InstrProfileMap.
1280e8d8bef9SDimitry Andric     InstrProfRecord *R = &PD.getValue().begin()->second;
1281bdd1243dSDimitry Andric     StringRef FullName = PD.getKey();
1282bdd1243dSDimitry Andric     InstrProfileMap[FullName] = InstrProfileEntry(R);
1283bdd1243dSDimitry Andric     buildStaticFuncMap(FullName);
1284bdd1243dSDimitry Andric   }
1285bdd1243dSDimitry Andric 
1286bdd1243dSDimitry Andric   for (auto &PD : Reader->getProfiles()) {
1287bdd1243dSDimitry Andric     sampleprof::FunctionSamples &FS = PD.second;
12885f757f3fSDimitry Andric     std::string Name = FS.getFunction().str();
12895f757f3fSDimitry Andric     BuildMaxSampleMap(FS, Name);
1290e8d8bef9SDimitry Andric   }
1291e8d8bef9SDimitry Andric 
1292e8d8bef9SDimitry Andric   ProfileSummary InstrPS = *IPBuilder.getSummary();
1293e8d8bef9SDimitry Andric   ProfileSummary SamplePS = Reader->getSummary();
1294e8d8bef9SDimitry Andric 
1295e8d8bef9SDimitry Andric   // Compute cold thresholds for instr profile and sample profile.
1296bdd1243dSDimitry Andric   uint64_t HotSampleThreshold =
1297bdd1243dSDimitry Andric       ProfileSummaryBuilder::getEntryForPercentile(
1298bdd1243dSDimitry Andric           SamplePS.getDetailedSummary(),
1299bdd1243dSDimitry Andric           ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx])
1300bdd1243dSDimitry Andric           .MinCount;
1301e8d8bef9SDimitry Andric   uint64_t ColdSampleThreshold =
1302e8d8bef9SDimitry Andric       ProfileSummaryBuilder::getEntryForPercentile(
1303e8d8bef9SDimitry Andric           SamplePS.getDetailedSummary(),
1304e8d8bef9SDimitry Andric           ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx])
1305e8d8bef9SDimitry Andric           .MinCount;
1306e8d8bef9SDimitry Andric   uint64_t HotInstrThreshold =
1307e8d8bef9SDimitry Andric       ProfileSummaryBuilder::getEntryForPercentile(
1308e8d8bef9SDimitry Andric           InstrPS.getDetailedSummary(),
1309e8d8bef9SDimitry Andric           ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx])
1310e8d8bef9SDimitry Andric           .MinCount;
1311e8d8bef9SDimitry Andric   uint64_t ColdInstrThreshold =
1312e8d8bef9SDimitry Andric       InstrProfColdThreshold
1313e8d8bef9SDimitry Andric           ? InstrProfColdThreshold
1314e8d8bef9SDimitry Andric           : ProfileSummaryBuilder::getEntryForPercentile(
1315e8d8bef9SDimitry Andric                 InstrPS.getDetailedSummary(),
1316e8d8bef9SDimitry Andric                 ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx])
1317e8d8bef9SDimitry Andric                 .MinCount;
1318e8d8bef9SDimitry Andric 
1319e8d8bef9SDimitry Andric   // Find hot/warm functions in sample profile which is cold in instr profile
1320e8d8bef9SDimitry Andric   // and adjust the profiles of those functions in the instr profile.
1321bdd1243dSDimitry Andric   for (const auto &E : FlattenSampleMap) {
1322bdd1243dSDimitry Andric     uint64_t SampleMaxCount = std::max(E.second.first, E.second.second);
1323bdd1243dSDimitry Andric     if (SampleMaxCount < ColdSampleThreshold)
1324bdd1243dSDimitry Andric       continue;
13255f757f3fSDimitry Andric     StringRef Name = E.first();
1326bdd1243dSDimitry Andric     auto It = InstrProfileMap.find(Name);
1327bdd1243dSDimitry Andric     if (It == InstrProfileMap.end()) {
1328bdd1243dSDimitry Andric       auto NewName = StaticFuncMap.find(Name);
1329bdd1243dSDimitry Andric       if (NewName != StaticFuncMap.end()) {
1330bdd1243dSDimitry Andric         It = InstrProfileMap.find(NewName->second.str());
1331bdd1243dSDimitry Andric         if (NewName->second == DuplicateNameStr) {
1332bdd1243dSDimitry Andric           WithColor::warning()
1333bdd1243dSDimitry Andric               << "Static function " << Name
1334bdd1243dSDimitry Andric               << " has multiple promoted names, cannot adjust profile.\n";
1335e8d8bef9SDimitry Andric         }
1336e8d8bef9SDimitry Andric       }
1337e8d8bef9SDimitry Andric     }
1338bdd1243dSDimitry Andric     if (It == InstrProfileMap.end() ||
1339bdd1243dSDimitry Andric         It->second.MaxCount > ColdInstrThreshold ||
1340bdd1243dSDimitry Andric         It->second.NumEdgeCounters < SupplMinSizeThreshold)
1341bdd1243dSDimitry Andric       continue;
1342bdd1243dSDimitry Andric     bool SetToHot = SampleMaxCount >= HotSampleThreshold;
1343bdd1243dSDimitry Andric     updateInstrProfileEntry(It->second, SetToHot, HotInstrThreshold,
1344bdd1243dSDimitry Andric                             ColdInstrThreshold, ZeroCounterThreshold);
1345bdd1243dSDimitry Andric   }
1346bdd1243dSDimitry Andric }
1347e8d8bef9SDimitry Andric 
1348e8d8bef9SDimitry Andric /// The main function to supplement instr profile with sample profile.
1349e8d8bef9SDimitry Andric /// \Inputs contains the instr profile. \p SampleFilename specifies the
1350e8d8bef9SDimitry Andric /// sample profile. \p OutputFilename specifies the output profile name.
1351e8d8bef9SDimitry Andric /// \p OutputFormat specifies the output profile format. \p OutputSparse
1352e8d8bef9SDimitry Andric /// specifies whether to generate sparse profile. \p SupplMinSizeThreshold
1353e8d8bef9SDimitry Andric /// specifies the minimal size for the functions whose profile will be
1354e8d8bef9SDimitry Andric /// adjusted. \p ZeroCounterThreshold is the threshold to check whether
1355e8d8bef9SDimitry Andric /// a function contains too many zero counters and whether its profile
1356e8d8bef9SDimitry Andric /// should be dropped. \p InstrProfColdThreshold is the user specified
1357e8d8bef9SDimitry Andric /// cold threshold which will override the cold threshold got from the
1358e8d8bef9SDimitry Andric /// instr profile summary.
supplementInstrProfile(const WeightedFileVector & Inputs,StringRef SampleFilename,bool OutputSparse,unsigned SupplMinSizeThreshold,float ZeroCounterThreshold,unsigned InstrProfColdThreshold)13595f757f3fSDimitry Andric static void supplementInstrProfile(const WeightedFileVector &Inputs,
13605f757f3fSDimitry Andric                                    StringRef SampleFilename, bool OutputSparse,
13615f757f3fSDimitry Andric                                    unsigned SupplMinSizeThreshold,
13625f757f3fSDimitry Andric                                    float ZeroCounterThreshold,
1363e8d8bef9SDimitry Andric                                    unsigned InstrProfColdThreshold) {
1364*0fca6ea1SDimitry Andric   if (OutputFilename == "-")
1365fe6060f1SDimitry Andric     exitWithError("cannot write indexed profdata format to stdout");
1366e8d8bef9SDimitry Andric   if (Inputs.size() != 1)
1367fe6060f1SDimitry Andric     exitWithError("expect one input to be an instr profile");
1368e8d8bef9SDimitry Andric   if (Inputs[0].Weight != 1)
1369fe6060f1SDimitry Andric     exitWithError("expect instr profile doesn't have weight");
1370e8d8bef9SDimitry Andric 
1371e8d8bef9SDimitry Andric   StringRef InstrFilename = Inputs[0].Filename;
1372e8d8bef9SDimitry Andric 
1373e8d8bef9SDimitry Andric   // Read sample profile.
1374e8d8bef9SDimitry Andric   LLVMContext Context;
137506c3fb27SDimitry Andric   auto FS = vfs::getRealFileSystem();
1376fe6060f1SDimitry Andric   auto ReaderOrErr = sampleprof::SampleProfileReader::create(
137706c3fb27SDimitry Andric       SampleFilename.str(), Context, *FS, FSDiscriminatorPassOption);
1378e8d8bef9SDimitry Andric   if (std::error_code EC = ReaderOrErr.getError())
1379e8d8bef9SDimitry Andric     exitWithErrorCode(EC, SampleFilename);
1380e8d8bef9SDimitry Andric   auto Reader = std::move(ReaderOrErr.get());
1381e8d8bef9SDimitry Andric   if (std::error_code EC = Reader->read())
1382e8d8bef9SDimitry Andric     exitWithErrorCode(EC, SampleFilename);
1383e8d8bef9SDimitry Andric 
1384e8d8bef9SDimitry Andric   // Read instr profile.
1385e8d8bef9SDimitry Andric   std::mutex ErrorLock;
1386e8d8bef9SDimitry Andric   SmallSet<instrprof_error, 4> WriterErrorCodes;
1387e8d8bef9SDimitry Andric   auto WC = std::make_unique<WriterContext>(OutputSparse, ErrorLock,
1388e8d8bef9SDimitry Andric                                             WriterErrorCodes);
138981ad6265SDimitry Andric   loadInput(Inputs[0], nullptr, nullptr, /*ProfiledBinary=*/"", WC.get());
1390e8d8bef9SDimitry Andric   if (WC->Errors.size() > 0)
1391e8d8bef9SDimitry Andric     exitWithError(std::move(WC->Errors[0].first), InstrFilename);
1392e8d8bef9SDimitry Andric 
1393e8d8bef9SDimitry Andric   adjustInstrProfile(WC, Reader, SupplMinSizeThreshold, ZeroCounterThreshold,
1394e8d8bef9SDimitry Andric                      InstrProfColdThreshold);
1395e8d8bef9SDimitry Andric   writeInstrProfile(OutputFilename, OutputFormat, WC->Writer);
1396e8d8bef9SDimitry Andric }
1397e8d8bef9SDimitry Andric 
13980b57cec5SDimitry Andric /// Make a copy of the given function samples with all symbol names remapped
13990b57cec5SDimitry Andric /// by the provided symbol remapper.
14000b57cec5SDimitry Andric static sampleprof::FunctionSamples
remapSamples(const sampleprof::FunctionSamples & Samples,SymbolRemapper & Remapper,sampleprof_error & Error)14010b57cec5SDimitry Andric remapSamples(const sampleprof::FunctionSamples &Samples,
14020b57cec5SDimitry Andric              SymbolRemapper &Remapper, sampleprof_error &Error) {
14030b57cec5SDimitry Andric   sampleprof::FunctionSamples Result;
14045f757f3fSDimitry Andric   Result.setFunction(Remapper(Samples.getFunction()));
14050b57cec5SDimitry Andric   Result.addTotalSamples(Samples.getTotalSamples());
14060b57cec5SDimitry Andric   Result.addHeadSamples(Samples.getHeadSamples());
14070b57cec5SDimitry Andric   for (const auto &BodySample : Samples.getBodySamples()) {
1408fe6060f1SDimitry Andric     uint32_t MaskedDiscriminator =
1409fe6060f1SDimitry Andric         BodySample.first.Discriminator & getDiscriminatorMask();
1410fe6060f1SDimitry Andric     Result.addBodySamples(BodySample.first.LineOffset, MaskedDiscriminator,
14110b57cec5SDimitry Andric                           BodySample.second.getSamples());
14120b57cec5SDimitry Andric     for (const auto &Target : BodySample.second.getCallTargets()) {
14130b57cec5SDimitry Andric       Result.addCalledTargetSamples(BodySample.first.LineOffset,
1414fe6060f1SDimitry Andric                                     MaskedDiscriminator,
14155f757f3fSDimitry Andric                                     Remapper(Target.first), Target.second);
14160b57cec5SDimitry Andric     }
14170b57cec5SDimitry Andric   }
14180b57cec5SDimitry Andric   for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) {
14190b57cec5SDimitry Andric     sampleprof::FunctionSamplesMap &Target =
14200b57cec5SDimitry Andric         Result.functionSamplesAt(CallsiteSamples.first);
14210b57cec5SDimitry Andric     for (const auto &Callsite : CallsiteSamples.second) {
14220b57cec5SDimitry Andric       sampleprof::FunctionSamples Remapped =
14230b57cec5SDimitry Andric           remapSamples(Callsite.second, Remapper, Error);
1424*0fca6ea1SDimitry Andric       mergeSampleProfErrors(Error,
1425*0fca6ea1SDimitry Andric                             Target[Remapped.getFunction()].merge(Remapped));
14260b57cec5SDimitry Andric     }
14270b57cec5SDimitry Andric   }
14280b57cec5SDimitry Andric   return Result;
14290b57cec5SDimitry Andric }
14300b57cec5SDimitry Andric 
14310b57cec5SDimitry Andric static sampleprof::SampleProfileFormat FormatMap[] = {
14328bcb0991SDimitry Andric     sampleprof::SPF_None,
14338bcb0991SDimitry Andric     sampleprof::SPF_Text,
143406c3fb27SDimitry Andric     sampleprof::SPF_None,
14358bcb0991SDimitry Andric     sampleprof::SPF_Ext_Binary,
14368bcb0991SDimitry Andric     sampleprof::SPF_GCC,
14378bcb0991SDimitry Andric     sampleprof::SPF_Binary};
14388bcb0991SDimitry Andric 
14398bcb0991SDimitry Andric static std::unique_ptr<MemoryBuffer>
getInputFileBuf(const StringRef & InputFile)14408bcb0991SDimitry Andric getInputFileBuf(const StringRef &InputFile) {
14418bcb0991SDimitry Andric   if (InputFile == "")
14428bcb0991SDimitry Andric     return {};
14438bcb0991SDimitry Andric 
14448bcb0991SDimitry Andric   auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);
14458bcb0991SDimitry Andric   if (!BufOrError)
14468bcb0991SDimitry Andric     exitWithErrorCode(BufOrError.getError(), InputFile);
14478bcb0991SDimitry Andric 
14488bcb0991SDimitry Andric   return std::move(*BufOrError);
14498bcb0991SDimitry Andric }
14508bcb0991SDimitry Andric 
populateProfileSymbolList(MemoryBuffer * Buffer,sampleprof::ProfileSymbolList & PSL)14518bcb0991SDimitry Andric static void populateProfileSymbolList(MemoryBuffer *Buffer,
14528bcb0991SDimitry Andric                                       sampleprof::ProfileSymbolList &PSL) {
14538bcb0991SDimitry Andric   if (!Buffer)
14548bcb0991SDimitry Andric     return;
14558bcb0991SDimitry Andric 
14568bcb0991SDimitry Andric   SmallVector<StringRef, 32> SymbolVec;
14578bcb0991SDimitry Andric   StringRef Data = Buffer->getBuffer();
14588bcb0991SDimitry Andric   Data.split(SymbolVec, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
14598bcb0991SDimitry Andric 
146004eeddc0SDimitry Andric   for (StringRef SymbolStr : SymbolVec)
146104eeddc0SDimitry Andric     PSL.add(SymbolStr.trim());
14628bcb0991SDimitry Andric }
14638bcb0991SDimitry Andric 
handleExtBinaryWriter(sampleprof::SampleProfileWriter & Writer,ProfileFormat OutputFormat,MemoryBuffer * Buffer,sampleprof::ProfileSymbolList & WriterList,bool CompressAllSections,bool UseMD5,bool GenPartialProfile)14648bcb0991SDimitry Andric static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer,
14658bcb0991SDimitry Andric                                   ProfileFormat OutputFormat,
14668bcb0991SDimitry Andric                                   MemoryBuffer *Buffer,
14678bcb0991SDimitry Andric                                   sampleprof::ProfileSymbolList &WriterList,
14685ffd83dbSDimitry Andric                                   bool CompressAllSections, bool UseMD5,
14695ffd83dbSDimitry Andric                                   bool GenPartialProfile) {
14708bcb0991SDimitry Andric   populateProfileSymbolList(Buffer, WriterList);
14718bcb0991SDimitry Andric   if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary)
14728bcb0991SDimitry Andric     warn("Profile Symbol list is not empty but the output format is not "
14738bcb0991SDimitry Andric          "ExtBinary format. The list will be lost in the output. ");
14748bcb0991SDimitry Andric 
14758bcb0991SDimitry Andric   Writer.setProfileSymbolList(&WriterList);
14768bcb0991SDimitry Andric 
14778bcb0991SDimitry Andric   if (CompressAllSections) {
14785ffd83dbSDimitry Andric     if (OutputFormat != PF_Ext_Binary)
14798bcb0991SDimitry Andric       warn("-compress-all-section is ignored. Specify -extbinary to enable it");
14805ffd83dbSDimitry Andric     else
14815ffd83dbSDimitry Andric       Writer.setToCompressAllSections();
14828bcb0991SDimitry Andric   }
14835ffd83dbSDimitry Andric   if (UseMD5) {
14845ffd83dbSDimitry Andric     if (OutputFormat != PF_Ext_Binary)
14855ffd83dbSDimitry Andric       warn("-use-md5 is ignored. Specify -extbinary to enable it");
14865ffd83dbSDimitry Andric     else
14875ffd83dbSDimitry Andric       Writer.setUseMD5();
14885ffd83dbSDimitry Andric   }
14895ffd83dbSDimitry Andric   if (GenPartialProfile) {
14905ffd83dbSDimitry Andric     if (OutputFormat != PF_Ext_Binary)
14915ffd83dbSDimitry Andric       warn("-gen-partial-profile is ignored. Specify -extbinary to enable it");
14925ffd83dbSDimitry Andric     else
14935ffd83dbSDimitry Andric       Writer.setPartialProfile();
14948bcb0991SDimitry Andric   }
14958bcb0991SDimitry Andric }
14960b57cec5SDimitry Andric 
mergeSampleProfile(const WeightedFileVector & Inputs,SymbolRemapper * Remapper,StringRef ProfileSymbolListFile,size_t OutputSizeLimit)14975f757f3fSDimitry Andric static void mergeSampleProfile(const WeightedFileVector &Inputs,
14985f757f3fSDimitry Andric                                SymbolRemapper *Remapper,
14995f757f3fSDimitry Andric                                StringRef ProfileSymbolListFile,
15005f757f3fSDimitry Andric                                size_t OutputSizeLimit) {
15010b57cec5SDimitry Andric   using namespace sampleprof;
1502349cc55cSDimitry Andric   SampleProfileMap ProfileMap;
15030b57cec5SDimitry Andric   SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers;
15040b57cec5SDimitry Andric   LLVMContext Context;
15058bcb0991SDimitry Andric   sampleprof::ProfileSymbolList WriterList;
1506bdd1243dSDimitry Andric   std::optional<bool> ProfileIsProbeBased;
1507bdd1243dSDimitry Andric   std::optional<bool> ProfileIsCS;
15080b57cec5SDimitry Andric   for (const auto &Input : Inputs) {
150906c3fb27SDimitry Andric     auto FS = vfs::getRealFileSystem();
151006c3fb27SDimitry Andric     auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context, *FS,
1511fe6060f1SDimitry Andric                                                    FSDiscriminatorPassOption);
15128bcb0991SDimitry Andric     if (std::error_code EC = ReaderOrErr.getError()) {
15138bcb0991SDimitry Andric       warnOrExitGivenError(FailMode, EC, Input.Filename);
15148bcb0991SDimitry Andric       continue;
15158bcb0991SDimitry Andric     }
15160b57cec5SDimitry Andric 
15170b57cec5SDimitry Andric     // We need to keep the readers around until after all the files are
15180b57cec5SDimitry Andric     // read so that we do not lose the function names stored in each
15190b57cec5SDimitry Andric     // reader's memory. The function names are needed to write out the
15200b57cec5SDimitry Andric     // merged profile map.
15210b57cec5SDimitry Andric     Readers.push_back(std::move(ReaderOrErr.get()));
15220b57cec5SDimitry Andric     const auto Reader = Readers.back().get();
15238bcb0991SDimitry Andric     if (std::error_code EC = Reader->read()) {
15248bcb0991SDimitry Andric       warnOrExitGivenError(FailMode, EC, Input.Filename);
15258bcb0991SDimitry Andric       Readers.pop_back();
15268bcb0991SDimitry Andric       continue;
15278bcb0991SDimitry Andric     }
15280b57cec5SDimitry Andric 
1529349cc55cSDimitry Andric     SampleProfileMap &Profiles = Reader->getProfiles();
153081ad6265SDimitry Andric     if (ProfileIsProbeBased &&
1531e8d8bef9SDimitry Andric         ProfileIsProbeBased != FunctionSamples::ProfileIsProbeBased)
1532e8d8bef9SDimitry Andric       exitWithError(
1533e8d8bef9SDimitry Andric           "cannot merge probe-based profile with non-probe-based profile");
1534e8d8bef9SDimitry Andric     ProfileIsProbeBased = FunctionSamples::ProfileIsProbeBased;
153581ad6265SDimitry Andric     if (ProfileIsCS && ProfileIsCS != FunctionSamples::ProfileIsCS)
1536fe6060f1SDimitry Andric       exitWithError("cannot merge CS profile with non-CS profile");
153781ad6265SDimitry Andric     ProfileIsCS = FunctionSamples::ProfileIsCS;
1538349cc55cSDimitry Andric     for (SampleProfileMap::iterator I = Profiles.begin(), E = Profiles.end();
15390b57cec5SDimitry Andric          I != E; ++I) {
15400b57cec5SDimitry Andric       sampleprof_error Result = sampleprof_error::success;
15410b57cec5SDimitry Andric       FunctionSamples Remapped =
15420b57cec5SDimitry Andric           Remapper ? remapSamples(I->second, *Remapper, Result)
15430b57cec5SDimitry Andric                    : FunctionSamples();
15440b57cec5SDimitry Andric       FunctionSamples &Samples = Remapper ? Remapped : I->second;
1545349cc55cSDimitry Andric       SampleContext FContext = Samples.getContext();
1546*0fca6ea1SDimitry Andric       mergeSampleProfErrors(Result,
1547*0fca6ea1SDimitry Andric                             ProfileMap[FContext].merge(Samples, Input.Weight));
15480b57cec5SDimitry Andric       if (Result != sampleprof_error::success) {
15490b57cec5SDimitry Andric         std::error_code EC = make_error_code(Result);
1550349cc55cSDimitry Andric         handleMergeWriterError(errorCodeToError(EC), Input.Filename,
1551349cc55cSDimitry Andric                                FContext.toString());
15520b57cec5SDimitry Andric       }
15530b57cec5SDimitry Andric     }
15548bcb0991SDimitry Andric 
1555bdd1243dSDimitry Andric     if (!DropProfileSymbolList) {
15568bcb0991SDimitry Andric       std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =
15578bcb0991SDimitry Andric           Reader->getProfileSymbolList();
15588bcb0991SDimitry Andric       if (ReaderList)
15598bcb0991SDimitry Andric         WriterList.merge(*ReaderList);
15600b57cec5SDimitry Andric     }
1561bdd1243dSDimitry Andric   }
1562fe6060f1SDimitry Andric 
156381ad6265SDimitry Andric   if (ProfileIsCS && (SampleMergeColdContext || SampleTrimColdContext)) {
1564fe6060f1SDimitry Andric     // Use threshold calculated from profile summary unless specified.
1565fe6060f1SDimitry Andric     SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
1566fe6060f1SDimitry Andric     auto Summary = Builder.computeSummaryForProfiles(ProfileMap);
1567fe6060f1SDimitry Andric     uint64_t SampleProfColdThreshold =
1568fe6060f1SDimitry Andric         ProfileSummaryBuilder::getColdCountThreshold(
1569fe6060f1SDimitry Andric             (Summary->getDetailedSummary()));
1570fe6060f1SDimitry Andric 
1571fe6060f1SDimitry Andric     // Trim and merge cold context profile using cold threshold above;
1572fe6060f1SDimitry Andric     SampleContextTrimmer(ProfileMap)
1573fe6060f1SDimitry Andric         .trimAndMergeColdContextProfiles(
1574fe6060f1SDimitry Andric             SampleProfColdThreshold, SampleTrimColdContext,
1575349cc55cSDimitry Andric             SampleMergeColdContext, SampleColdContextFrameDepth, false);
1576fe6060f1SDimitry Andric   }
1577fe6060f1SDimitry Andric 
157806c3fb27SDimitry Andric   if (ProfileLayout == llvm::sampleprof::SPL_Flat) {
157906c3fb27SDimitry Andric     ProfileConverter::flattenProfile(ProfileMap, FunctionSamples::ProfileIsCS);
158006c3fb27SDimitry Andric     ProfileIsCS = FunctionSamples::ProfileIsCS = false;
158106c3fb27SDimitry Andric   } else if (ProfileIsCS && ProfileLayout == llvm::sampleprof::SPL_Nest) {
158206c3fb27SDimitry Andric     ProfileConverter CSConverter(ProfileMap);
158306c3fb27SDimitry Andric     CSConverter.convertCSProfiles();
158481ad6265SDimitry Andric     ProfileIsCS = FunctionSamples::ProfileIsCS = false;
15850eae32dcSDimitry Andric   }
15860eae32dcSDimitry Andric 
15877a6dacacSDimitry Andric   filterFunctions(ProfileMap);
15887a6dacacSDimitry Andric 
15890b57cec5SDimitry Andric   auto WriterOrErr =
15900b57cec5SDimitry Andric       SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]);
15910b57cec5SDimitry Andric   if (std::error_code EC = WriterOrErr.getError())
15920b57cec5SDimitry Andric     exitWithErrorCode(EC, OutputFilename);
15930b57cec5SDimitry Andric 
15940b57cec5SDimitry Andric   auto Writer = std::move(WriterOrErr.get());
15958bcb0991SDimitry Andric   // WriterList will have StringRef refering to string in Buffer.
15968bcb0991SDimitry Andric   // Make sure Buffer lives as long as WriterList.
15978bcb0991SDimitry Andric   auto Buffer = getInputFileBuf(ProfileSymbolListFile);
15988bcb0991SDimitry Andric   handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList,
15995ffd83dbSDimitry Andric                         CompressAllSections, UseMD5, GenPartialProfile);
160006c3fb27SDimitry Andric 
160106c3fb27SDimitry Andric   // If OutputSizeLimit is 0 (default), it is the same as write().
160206c3fb27SDimitry Andric   if (std::error_code EC =
160306c3fb27SDimitry Andric           Writer->writeWithSizeLimit(ProfileMap, OutputSizeLimit))
1604*0fca6ea1SDimitry Andric     exitWithErrorCode(EC);
16050b57cec5SDimitry Andric }
16060b57cec5SDimitry Andric 
parseWeightedFile(const StringRef & WeightedFilename)16070b57cec5SDimitry Andric static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) {
16080b57cec5SDimitry Andric   StringRef WeightStr, FileName;
16090b57cec5SDimitry Andric   std::tie(WeightStr, FileName) = WeightedFilename.split(',');
16100b57cec5SDimitry Andric 
16110b57cec5SDimitry Andric   uint64_t Weight;
16120b57cec5SDimitry Andric   if (WeightStr.getAsInteger(10, Weight) || Weight < 1)
1613fe6060f1SDimitry Andric     exitWithError("input weight must be a positive integer");
16140b57cec5SDimitry Andric 
16155ffd83dbSDimitry Andric   return {std::string(FileName), Weight};
16160b57cec5SDimitry Andric }
16170b57cec5SDimitry Andric 
addWeightedInput(WeightedFileVector & WNI,const WeightedFile & WF)16180b57cec5SDimitry Andric static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) {
16190b57cec5SDimitry Andric   StringRef Filename = WF.Filename;
16200b57cec5SDimitry Andric   uint64_t Weight = WF.Weight;
16210b57cec5SDimitry Andric 
16220b57cec5SDimitry Andric   // If it's STDIN just pass it on.
16230b57cec5SDimitry Andric   if (Filename == "-") {
16245ffd83dbSDimitry Andric     WNI.push_back({std::string(Filename), Weight});
16250b57cec5SDimitry Andric     return;
16260b57cec5SDimitry Andric   }
16270b57cec5SDimitry Andric 
16280b57cec5SDimitry Andric   llvm::sys::fs::file_status Status;
16290b57cec5SDimitry Andric   llvm::sys::fs::status(Filename, Status);
16300b57cec5SDimitry Andric   if (!llvm::sys::fs::exists(Status))
16310b57cec5SDimitry Andric     exitWithErrorCode(make_error_code(errc::no_such_file_or_directory),
16320b57cec5SDimitry Andric                       Filename);
16330b57cec5SDimitry Andric   // If it's a source file, collect it.
16340b57cec5SDimitry Andric   if (llvm::sys::fs::is_regular_file(Status)) {
16355ffd83dbSDimitry Andric     WNI.push_back({std::string(Filename), Weight});
16360b57cec5SDimitry Andric     return;
16370b57cec5SDimitry Andric   }
16380b57cec5SDimitry Andric 
16390b57cec5SDimitry Andric   if (llvm::sys::fs::is_directory(Status)) {
16400b57cec5SDimitry Andric     std::error_code EC;
16410b57cec5SDimitry Andric     for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E;
16420b57cec5SDimitry Andric          F != E && !EC; F.increment(EC)) {
16430b57cec5SDimitry Andric       if (llvm::sys::fs::is_regular_file(F->path())) {
16440b57cec5SDimitry Andric         addWeightedInput(WNI, {F->path(), Weight});
16450b57cec5SDimitry Andric       }
16460b57cec5SDimitry Andric     }
16470b57cec5SDimitry Andric     if (EC)
16480b57cec5SDimitry Andric       exitWithErrorCode(EC, Filename);
16490b57cec5SDimitry Andric   }
16500b57cec5SDimitry Andric }
16510b57cec5SDimitry Andric 
parseInputFilenamesFile(MemoryBuffer * Buffer,WeightedFileVector & WFV)16520b57cec5SDimitry Andric static void parseInputFilenamesFile(MemoryBuffer *Buffer,
16530b57cec5SDimitry Andric                                     WeightedFileVector &WFV) {
16540b57cec5SDimitry Andric   if (!Buffer)
16550b57cec5SDimitry Andric     return;
16560b57cec5SDimitry Andric 
16570b57cec5SDimitry Andric   SmallVector<StringRef, 8> Entries;
16580b57cec5SDimitry Andric   StringRef Data = Buffer->getBuffer();
16590b57cec5SDimitry Andric   Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
16600b57cec5SDimitry Andric   for (const StringRef &FileWeightEntry : Entries) {
16610b57cec5SDimitry Andric     StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r");
16620b57cec5SDimitry Andric     // Skip comments.
16635f757f3fSDimitry Andric     if (SanitizedEntry.starts_with("#"))
16640b57cec5SDimitry Andric       continue;
16650b57cec5SDimitry Andric     // If there's no comma, it's an unweighted profile.
1666349cc55cSDimitry Andric     else if (!SanitizedEntry.contains(','))
16675ffd83dbSDimitry Andric       addWeightedInput(WFV, {std::string(SanitizedEntry), 1});
16680b57cec5SDimitry Andric     else
16690b57cec5SDimitry Andric       addWeightedInput(WFV, parseWeightedFile(SanitizedEntry));
16700b57cec5SDimitry Andric   }
16710b57cec5SDimitry Andric }
16720b57cec5SDimitry Andric 
merge_main(StringRef ProgName)1673*0fca6ea1SDimitry Andric static int merge_main(StringRef ProgName) {
16740b57cec5SDimitry Andric   WeightedFileVector WeightedInputs;
16750b57cec5SDimitry Andric   for (StringRef Filename : InputFilenames)
16765ffd83dbSDimitry Andric     addWeightedInput(WeightedInputs, {std::string(Filename), 1});
16770b57cec5SDimitry Andric   for (StringRef WeightedFilename : WeightedInputFilenames)
16780b57cec5SDimitry Andric     addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename));
16790b57cec5SDimitry Andric 
16800b57cec5SDimitry Andric   // Make sure that the file buffer stays alive for the duration of the
16810b57cec5SDimitry Andric   // weighted input vector's lifetime.
16828bcb0991SDimitry Andric   auto Buffer = getInputFileBuf(InputFilenamesFile);
16830b57cec5SDimitry Andric   parseInputFilenamesFile(Buffer.get(), WeightedInputs);
16840b57cec5SDimitry Andric 
16850b57cec5SDimitry Andric   if (WeightedInputs.empty())
1686*0fca6ea1SDimitry Andric     exitWithError("no input files specified. See " + ProgName + " merge -help");
16870b57cec5SDimitry Andric 
16880b57cec5SDimitry Andric   if (DumpInputFileList) {
16890b57cec5SDimitry Andric     for (auto &WF : WeightedInputs)
16900b57cec5SDimitry Andric       outs() << WF.Weight << "," << WF.Filename << "\n";
16910b57cec5SDimitry Andric     return 0;
16920b57cec5SDimitry Andric   }
16930b57cec5SDimitry Andric 
16940b57cec5SDimitry Andric   std::unique_ptr<SymbolRemapper> Remapper;
16950b57cec5SDimitry Andric   if (!RemappingFile.empty())
16960b57cec5SDimitry Andric     Remapper = SymbolRemapper::create(RemappingFile);
16970b57cec5SDimitry Andric 
1698e8d8bef9SDimitry Andric   if (!SupplInstrWithSample.empty()) {
1699e8d8bef9SDimitry Andric     if (ProfileKind != instr)
1700e8d8bef9SDimitry Andric       exitWithError(
1701e8d8bef9SDimitry Andric           "-supplement-instr-with-sample can only work with -instr. ");
1702e8d8bef9SDimitry Andric 
17035f757f3fSDimitry Andric     supplementInstrProfile(WeightedInputs, SupplInstrWithSample, OutputSparse,
17045f757f3fSDimitry Andric                            SupplMinSizeThreshold, ZeroCounterThreshold,
17055f757f3fSDimitry Andric                            InstrProfColdThreshold);
1706e8d8bef9SDimitry Andric     return 0;
1707e8d8bef9SDimitry Andric   }
1708e8d8bef9SDimitry Andric 
17090b57cec5SDimitry Andric   if (ProfileKind == instr)
17105f757f3fSDimitry Andric     mergeInstrProfile(WeightedInputs, Remapper.get(), MaxDbgCorrelationWarnings,
17115f757f3fSDimitry Andric                       ProfiledBinary);
17120b57cec5SDimitry Andric   else
17135f757f3fSDimitry Andric     mergeSampleProfile(WeightedInputs, Remapper.get(), ProfileSymbolListFile,
17145f757f3fSDimitry Andric                        OutputSizeLimit);
17150b57cec5SDimitry Andric   return 0;
17160b57cec5SDimitry Andric }
17170b57cec5SDimitry Andric 
17180b57cec5SDimitry 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)17190b57cec5SDimitry Andric static void overlapInstrProfile(const std::string &BaseFilename,
17200b57cec5SDimitry Andric                                 const std::string &TestFilename,
17210b57cec5SDimitry Andric                                 const OverlapFuncFilters &FuncFilter,
17220b57cec5SDimitry Andric                                 raw_fd_ostream &OS, bool IsCS) {
17230b57cec5SDimitry Andric   std::mutex ErrorLock;
17240b57cec5SDimitry Andric   SmallSet<instrprof_error, 4> WriterErrorCodes;
17250b57cec5SDimitry Andric   WriterContext Context(false, ErrorLock, WriterErrorCodes);
17260b57cec5SDimitry Andric   WeightedFile WeightedInput{BaseFilename, 1};
17270b57cec5SDimitry Andric   OverlapStats Overlap;
17288bcb0991SDimitry Andric   Error E = Overlap.accumulateCounts(BaseFilename, TestFilename, IsCS);
17290b57cec5SDimitry Andric   if (E)
1730fe6060f1SDimitry Andric     exitWithError(std::move(E), "error in getting profile count sums");
17310b57cec5SDimitry Andric   if (Overlap.Base.CountSum < 1.0f) {
17320b57cec5SDimitry Andric     OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n";
17330b57cec5SDimitry Andric     exit(0);
17340b57cec5SDimitry Andric   }
17350b57cec5SDimitry Andric   if (Overlap.Test.CountSum < 1.0f) {
17360b57cec5SDimitry Andric     OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n";
17370b57cec5SDimitry Andric     exit(0);
17380b57cec5SDimitry Andric   }
173981ad6265SDimitry Andric   loadInput(WeightedInput, nullptr, nullptr, /*ProfiledBinary=*/"", &Context);
17400b57cec5SDimitry Andric   overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS,
17410b57cec5SDimitry Andric                IsCS);
17420b57cec5SDimitry Andric   Overlap.dump(OS);
17430b57cec5SDimitry Andric }
17440b57cec5SDimitry Andric 
1745e8d8bef9SDimitry Andric namespace {
1746e8d8bef9SDimitry Andric struct SampleOverlapStats {
1747349cc55cSDimitry Andric   SampleContext BaseName;
1748349cc55cSDimitry Andric   SampleContext TestName;
1749e8d8bef9SDimitry Andric   // Number of overlap units
17505f757f3fSDimitry Andric   uint64_t OverlapCount = 0;
1751e8d8bef9SDimitry Andric   // Total samples of overlap units
17525f757f3fSDimitry Andric   uint64_t OverlapSample = 0;
1753e8d8bef9SDimitry Andric   // Number of and total samples of units that only present in base or test
1754e8d8bef9SDimitry Andric   // profile
17555f757f3fSDimitry Andric   uint64_t BaseUniqueCount = 0;
17565f757f3fSDimitry Andric   uint64_t BaseUniqueSample = 0;
17575f757f3fSDimitry Andric   uint64_t TestUniqueCount = 0;
17585f757f3fSDimitry Andric   uint64_t TestUniqueSample = 0;
1759e8d8bef9SDimitry Andric   // Number of units and total samples in base or test profile
17605f757f3fSDimitry Andric   uint64_t BaseCount = 0;
17615f757f3fSDimitry Andric   uint64_t BaseSample = 0;
17625f757f3fSDimitry Andric   uint64_t TestCount = 0;
17635f757f3fSDimitry Andric   uint64_t TestSample = 0;
1764e8d8bef9SDimitry Andric   // Number of and total samples of units that present in at least one profile
17655f757f3fSDimitry Andric   uint64_t UnionCount = 0;
17665f757f3fSDimitry Andric   uint64_t UnionSample = 0;
1767e8d8bef9SDimitry Andric   // Weighted similarity
17685f757f3fSDimitry Andric   double Similarity = 0.0;
1769e8d8bef9SDimitry Andric   // For SampleOverlapStats instances representing functions, weights of the
1770e8d8bef9SDimitry Andric   // function in base and test profiles
17715f757f3fSDimitry Andric   double BaseWeight = 0.0;
17725f757f3fSDimitry Andric   double TestWeight = 0.0;
1773e8d8bef9SDimitry Andric 
17745f757f3fSDimitry Andric   SampleOverlapStats() = default;
1775e8d8bef9SDimitry Andric };
1776e8d8bef9SDimitry Andric } // end anonymous namespace
1777e8d8bef9SDimitry Andric 
1778e8d8bef9SDimitry Andric namespace {
1779e8d8bef9SDimitry Andric struct FuncSampleStats {
1780cb14a3feSDimitry Andric   uint64_t SampleSum = 0;
1781cb14a3feSDimitry Andric   uint64_t MaxSample = 0;
1782cb14a3feSDimitry Andric   uint64_t HotBlockCount = 0;
1783cb14a3feSDimitry Andric   FuncSampleStats() = default;
FuncSampleStats__anon209d874b1011::FuncSampleStats1784e8d8bef9SDimitry Andric   FuncSampleStats(uint64_t SampleSum, uint64_t MaxSample,
1785e8d8bef9SDimitry Andric                   uint64_t HotBlockCount)
1786e8d8bef9SDimitry Andric       : SampleSum(SampleSum), MaxSample(MaxSample),
1787e8d8bef9SDimitry Andric         HotBlockCount(HotBlockCount) {}
1788e8d8bef9SDimitry Andric };
1789e8d8bef9SDimitry Andric } // end anonymous namespace
1790e8d8bef9SDimitry Andric 
1791e8d8bef9SDimitry Andric namespace {
1792e8d8bef9SDimitry Andric enum MatchStatus { MS_Match, MS_FirstUnique, MS_SecondUnique, MS_None };
1793e8d8bef9SDimitry Andric 
1794e8d8bef9SDimitry Andric // Class for updating merging steps for two sorted maps. The class should be
1795e8d8bef9SDimitry Andric // instantiated with a map iterator type.
1796e8d8bef9SDimitry Andric template <class T> class MatchStep {
1797e8d8bef9SDimitry Andric public:
1798e8d8bef9SDimitry Andric   MatchStep() = delete;
1799e8d8bef9SDimitry Andric 
MatchStep(T FirstIter,T FirstEnd,T SecondIter,T SecondEnd)1800e8d8bef9SDimitry Andric   MatchStep(T FirstIter, T FirstEnd, T SecondIter, T SecondEnd)
1801e8d8bef9SDimitry Andric       : FirstIter(FirstIter), FirstEnd(FirstEnd), SecondIter(SecondIter),
1802e8d8bef9SDimitry Andric         SecondEnd(SecondEnd), Status(MS_None) {}
1803e8d8bef9SDimitry Andric 
areBothFinished() const1804e8d8bef9SDimitry Andric   bool areBothFinished() const {
1805e8d8bef9SDimitry Andric     return (FirstIter == FirstEnd && SecondIter == SecondEnd);
1806e8d8bef9SDimitry Andric   }
1807e8d8bef9SDimitry Andric 
isFirstFinished() const1808e8d8bef9SDimitry Andric   bool isFirstFinished() const { return FirstIter == FirstEnd; }
1809e8d8bef9SDimitry Andric 
isSecondFinished() const1810e8d8bef9SDimitry Andric   bool isSecondFinished() const { return SecondIter == SecondEnd; }
1811e8d8bef9SDimitry Andric 
1812e8d8bef9SDimitry Andric   /// Advance one step based on the previous match status unless the previous
1813e8d8bef9SDimitry Andric   /// status is MS_None. Then update Status based on the comparison between two
1814e8d8bef9SDimitry Andric   /// container iterators at the current step. If the previous status is
1815e8d8bef9SDimitry Andric   /// MS_None, it means two iterators are at the beginning and no comparison has
1816e8d8bef9SDimitry Andric   /// been made, so we simply update Status without advancing the iterators.
1817e8d8bef9SDimitry Andric   void updateOneStep();
1818e8d8bef9SDimitry Andric 
getFirstIter() const1819e8d8bef9SDimitry Andric   T getFirstIter() const { return FirstIter; }
1820e8d8bef9SDimitry Andric 
getSecondIter() const1821e8d8bef9SDimitry Andric   T getSecondIter() const { return SecondIter; }
1822e8d8bef9SDimitry Andric 
getMatchStatus() const1823e8d8bef9SDimitry Andric   MatchStatus getMatchStatus() const { return Status; }
1824e8d8bef9SDimitry Andric 
1825e8d8bef9SDimitry Andric private:
1826e8d8bef9SDimitry Andric   // Current iterator and end iterator of the first container.
1827e8d8bef9SDimitry Andric   T FirstIter;
1828e8d8bef9SDimitry Andric   T FirstEnd;
1829e8d8bef9SDimitry Andric   // Current iterator and end iterator of the second container.
1830e8d8bef9SDimitry Andric   T SecondIter;
1831e8d8bef9SDimitry Andric   T SecondEnd;
1832e8d8bef9SDimitry Andric   // Match status of the current step.
1833e8d8bef9SDimitry Andric   MatchStatus Status;
1834e8d8bef9SDimitry Andric };
1835e8d8bef9SDimitry Andric } // end anonymous namespace
1836e8d8bef9SDimitry Andric 
updateOneStep()1837e8d8bef9SDimitry Andric template <class T> void MatchStep<T>::updateOneStep() {
1838e8d8bef9SDimitry Andric   switch (Status) {
1839e8d8bef9SDimitry Andric   case MS_Match:
1840e8d8bef9SDimitry Andric     ++FirstIter;
1841e8d8bef9SDimitry Andric     ++SecondIter;
1842e8d8bef9SDimitry Andric     break;
1843e8d8bef9SDimitry Andric   case MS_FirstUnique:
1844e8d8bef9SDimitry Andric     ++FirstIter;
1845e8d8bef9SDimitry Andric     break;
1846e8d8bef9SDimitry Andric   case MS_SecondUnique:
1847e8d8bef9SDimitry Andric     ++SecondIter;
1848e8d8bef9SDimitry Andric     break;
1849e8d8bef9SDimitry Andric   case MS_None:
1850e8d8bef9SDimitry Andric     break;
1851e8d8bef9SDimitry Andric   }
1852e8d8bef9SDimitry Andric 
1853e8d8bef9SDimitry Andric   // Update Status according to iterators at the current step.
1854e8d8bef9SDimitry Andric   if (areBothFinished())
1855e8d8bef9SDimitry Andric     return;
1856e8d8bef9SDimitry Andric   if (FirstIter != FirstEnd &&
1857e8d8bef9SDimitry Andric       (SecondIter == SecondEnd || FirstIter->first < SecondIter->first))
1858e8d8bef9SDimitry Andric     Status = MS_FirstUnique;
1859e8d8bef9SDimitry Andric   else if (SecondIter != SecondEnd &&
1860e8d8bef9SDimitry Andric            (FirstIter == FirstEnd || SecondIter->first < FirstIter->first))
1861e8d8bef9SDimitry Andric     Status = MS_SecondUnique;
1862e8d8bef9SDimitry Andric   else
1863e8d8bef9SDimitry Andric     Status = MS_Match;
1864e8d8bef9SDimitry Andric }
1865e8d8bef9SDimitry Andric 
1866e8d8bef9SDimitry Andric // Return the sum of line/block samples, the max line/block sample, and the
1867e8d8bef9SDimitry Andric // number of line/block samples above the given threshold in a function
1868e8d8bef9SDimitry Andric // including its inlinees.
getFuncSampleStats(const sampleprof::FunctionSamples & Func,FuncSampleStats & FuncStats,uint64_t HotThreshold)1869e8d8bef9SDimitry Andric static void getFuncSampleStats(const sampleprof::FunctionSamples &Func,
1870e8d8bef9SDimitry Andric                                FuncSampleStats &FuncStats,
1871e8d8bef9SDimitry Andric                                uint64_t HotThreshold) {
1872e8d8bef9SDimitry Andric   for (const auto &L : Func.getBodySamples()) {
1873e8d8bef9SDimitry Andric     uint64_t Sample = L.second.getSamples();
1874e8d8bef9SDimitry Andric     FuncStats.SampleSum += Sample;
1875e8d8bef9SDimitry Andric     FuncStats.MaxSample = std::max(FuncStats.MaxSample, Sample);
1876e8d8bef9SDimitry Andric     if (Sample >= HotThreshold)
1877e8d8bef9SDimitry Andric       ++FuncStats.HotBlockCount;
1878e8d8bef9SDimitry Andric   }
1879e8d8bef9SDimitry Andric 
1880e8d8bef9SDimitry Andric   for (const auto &C : Func.getCallsiteSamples()) {
1881e8d8bef9SDimitry Andric     for (const auto &F : C.second)
1882e8d8bef9SDimitry Andric       getFuncSampleStats(F.second, FuncStats, HotThreshold);
1883e8d8bef9SDimitry Andric   }
1884e8d8bef9SDimitry Andric }
1885e8d8bef9SDimitry Andric 
1886e8d8bef9SDimitry Andric /// Predicate that determines if a function is hot with a given threshold. We
1887e8d8bef9SDimitry Andric /// keep it separate from its callsites for possible extension in the future.
isFunctionHot(const FuncSampleStats & FuncStats,uint64_t HotThreshold)1888e8d8bef9SDimitry Andric static bool isFunctionHot(const FuncSampleStats &FuncStats,
1889e8d8bef9SDimitry Andric                           uint64_t HotThreshold) {
1890e8d8bef9SDimitry Andric   // We intentionally compare the maximum sample count in a function with the
1891e8d8bef9SDimitry Andric   // HotThreshold to get an approximate determination on hot functions.
1892e8d8bef9SDimitry Andric   return (FuncStats.MaxSample >= HotThreshold);
1893e8d8bef9SDimitry Andric }
1894e8d8bef9SDimitry Andric 
1895e8d8bef9SDimitry Andric namespace {
1896e8d8bef9SDimitry Andric class SampleOverlapAggregator {
1897e8d8bef9SDimitry Andric public:
SampleOverlapAggregator(const std::string & BaseFilename,const std::string & TestFilename,double LowSimilarityThreshold,double Epsilon,const OverlapFuncFilters & FuncFilter)1898e8d8bef9SDimitry Andric   SampleOverlapAggregator(const std::string &BaseFilename,
1899e8d8bef9SDimitry Andric                           const std::string &TestFilename,
1900e8d8bef9SDimitry Andric                           double LowSimilarityThreshold, double Epsilon,
1901e8d8bef9SDimitry Andric                           const OverlapFuncFilters &FuncFilter)
1902e8d8bef9SDimitry Andric       : BaseFilename(BaseFilename), TestFilename(TestFilename),
1903e8d8bef9SDimitry Andric         LowSimilarityThreshold(LowSimilarityThreshold), Epsilon(Epsilon),
1904e8d8bef9SDimitry Andric         FuncFilter(FuncFilter) {}
1905e8d8bef9SDimitry Andric 
1906e8d8bef9SDimitry Andric   /// Detect 0-sample input profile and report to output stream. This interface
1907e8d8bef9SDimitry Andric   /// should be called after loadProfiles().
1908e8d8bef9SDimitry Andric   bool detectZeroSampleProfile(raw_fd_ostream &OS) const;
1909e8d8bef9SDimitry Andric 
1910e8d8bef9SDimitry Andric   /// Write out function-level similarity statistics for functions specified by
1911e8d8bef9SDimitry Andric   /// options --function, --value-cutoff, and --similarity-cutoff.
1912e8d8bef9SDimitry Andric   void dumpFuncSimilarity(raw_fd_ostream &OS) const;
1913e8d8bef9SDimitry Andric 
1914e8d8bef9SDimitry Andric   /// Write out program-level similarity and overlap statistics.
1915e8d8bef9SDimitry Andric   void dumpProgramSummary(raw_fd_ostream &OS) const;
1916e8d8bef9SDimitry Andric 
1917e8d8bef9SDimitry Andric   /// Write out hot-function and hot-block statistics for base_profile,
1918e8d8bef9SDimitry Andric   /// test_profile, and their overlap. For both cases, the overlap HO is
1919e8d8bef9SDimitry Andric   /// calculated as follows:
1920e8d8bef9SDimitry Andric   ///    Given the number of functions (or blocks) that are hot in both profiles
1921e8d8bef9SDimitry Andric   ///    HCommon and the number of functions (or blocks) that are hot in at
1922e8d8bef9SDimitry Andric   ///    least one profile HUnion, HO = HCommon / HUnion.
1923e8d8bef9SDimitry Andric   void dumpHotFuncAndBlockOverlap(raw_fd_ostream &OS) const;
1924e8d8bef9SDimitry Andric 
1925e8d8bef9SDimitry Andric   /// This function tries matching functions in base and test profiles. For each
1926e8d8bef9SDimitry Andric   /// pair of matched functions, it aggregates the function-level
1927e8d8bef9SDimitry Andric   /// similarity into a profile-level similarity. It also dump function-level
1928e8d8bef9SDimitry Andric   /// similarity information of functions specified by --function,
1929e8d8bef9SDimitry Andric   /// --value-cutoff, and --similarity-cutoff options. The program-level
1930e8d8bef9SDimitry Andric   /// similarity PS is computed as follows:
1931e8d8bef9SDimitry Andric   ///     Given function-level similarity FS(A) for all function A, the
1932e8d8bef9SDimitry Andric   ///     weight of function A in base profile WB(A), and the weight of function
1933e8d8bef9SDimitry Andric   ///     A in test profile WT(A), compute PS(base_profile, test_profile) =
1934e8d8bef9SDimitry Andric   ///     sum_A(FS(A) * avg(WB(A), WT(A))) ranging in [0.0f to 1.0f] with 0.0
1935e8d8bef9SDimitry Andric   ///     meaning no-overlap.
1936e8d8bef9SDimitry Andric   void computeSampleProfileOverlap(raw_fd_ostream &OS);
1937e8d8bef9SDimitry Andric 
1938e8d8bef9SDimitry Andric   /// Initialize ProfOverlap with the sum of samples in base and test
1939e8d8bef9SDimitry Andric   /// profiles. This function also computes and keeps the sum of samples and
1940e8d8bef9SDimitry Andric   /// max sample counts of each function in BaseStats and TestStats for later
1941e8d8bef9SDimitry Andric   /// use to avoid re-computations.
1942e8d8bef9SDimitry Andric   void initializeSampleProfileOverlap();
1943e8d8bef9SDimitry Andric 
1944e8d8bef9SDimitry Andric   /// Load profiles specified by BaseFilename and TestFilename.
1945e8d8bef9SDimitry Andric   std::error_code loadProfiles();
1946e8d8bef9SDimitry Andric 
1947349cc55cSDimitry Andric   using FuncSampleStatsMap =
1948349cc55cSDimitry Andric       std::unordered_map<SampleContext, FuncSampleStats, SampleContext::Hash>;
1949349cc55cSDimitry Andric 
1950e8d8bef9SDimitry Andric private:
1951e8d8bef9SDimitry Andric   SampleOverlapStats ProfOverlap;
1952e8d8bef9SDimitry Andric   SampleOverlapStats HotFuncOverlap;
1953e8d8bef9SDimitry Andric   SampleOverlapStats HotBlockOverlap;
1954e8d8bef9SDimitry Andric   std::string BaseFilename;
1955e8d8bef9SDimitry Andric   std::string TestFilename;
1956e8d8bef9SDimitry Andric   std::unique_ptr<sampleprof::SampleProfileReader> BaseReader;
1957e8d8bef9SDimitry Andric   std::unique_ptr<sampleprof::SampleProfileReader> TestReader;
1958e8d8bef9SDimitry Andric   // BaseStats and TestStats hold FuncSampleStats for each function, with
1959e8d8bef9SDimitry Andric   // function name as the key.
1960349cc55cSDimitry Andric   FuncSampleStatsMap BaseStats;
1961349cc55cSDimitry Andric   FuncSampleStatsMap TestStats;
1962e8d8bef9SDimitry Andric   // Low similarity threshold in floating point number
1963e8d8bef9SDimitry Andric   double LowSimilarityThreshold;
1964e8d8bef9SDimitry Andric   // Block samples above BaseHotThreshold or TestHotThreshold are considered hot
1965e8d8bef9SDimitry Andric   // for tracking hot blocks.
1966e8d8bef9SDimitry Andric   uint64_t BaseHotThreshold;
1967e8d8bef9SDimitry Andric   uint64_t TestHotThreshold;
1968e8d8bef9SDimitry Andric   // A small threshold used to round the results of floating point accumulations
1969e8d8bef9SDimitry Andric   // to resolve imprecision.
1970e8d8bef9SDimitry Andric   const double Epsilon;
1971e8d8bef9SDimitry Andric   std::multimap<double, SampleOverlapStats, std::greater<double>>
1972e8d8bef9SDimitry Andric       FuncSimilarityDump;
1973e8d8bef9SDimitry Andric   // FuncFilter carries specifications in options --value-cutoff and
1974e8d8bef9SDimitry Andric   // --function.
1975e8d8bef9SDimitry Andric   OverlapFuncFilters FuncFilter;
1976e8d8bef9SDimitry Andric   // Column offsets for printing the function-level details table.
1977e8d8bef9SDimitry Andric   static const unsigned int TestWeightCol = 15;
1978e8d8bef9SDimitry Andric   static const unsigned int SimilarityCol = 30;
1979e8d8bef9SDimitry Andric   static const unsigned int OverlapCol = 43;
1980e8d8bef9SDimitry Andric   static const unsigned int BaseUniqueCol = 53;
1981e8d8bef9SDimitry Andric   static const unsigned int TestUniqueCol = 67;
1982e8d8bef9SDimitry Andric   static const unsigned int BaseSampleCol = 81;
1983e8d8bef9SDimitry Andric   static const unsigned int TestSampleCol = 96;
1984e8d8bef9SDimitry Andric   static const unsigned int FuncNameCol = 111;
1985e8d8bef9SDimitry Andric 
1986e8d8bef9SDimitry Andric   /// Return a similarity of two line/block sample counters in the same
1987e8d8bef9SDimitry Andric   /// function in base and test profiles. The line/block-similarity BS(i) is
1988e8d8bef9SDimitry Andric   /// computed as follows:
1989e8d8bef9SDimitry Andric   ///    For an offsets i, given the sample count at i in base profile BB(i),
1990e8d8bef9SDimitry Andric   ///    the sample count at i in test profile BT(i), the sum of sample counts
1991e8d8bef9SDimitry Andric   ///    in this function in base profile SB, and the sum of sample counts in
1992e8d8bef9SDimitry Andric   ///    this function in test profile ST, compute BS(i) = 1.0 - fabs(BB(i)/SB -
1993e8d8bef9SDimitry Andric   ///    BT(i)/ST), ranging in [0.0f to 1.0f] with 0.0 meaning no-overlap.
1994e8d8bef9SDimitry Andric   double computeBlockSimilarity(uint64_t BaseSample, uint64_t TestSample,
1995e8d8bef9SDimitry Andric                                 const SampleOverlapStats &FuncOverlap) const;
1996e8d8bef9SDimitry Andric 
1997e8d8bef9SDimitry Andric   void updateHotBlockOverlap(uint64_t BaseSample, uint64_t TestSample,
1998e8d8bef9SDimitry Andric                              uint64_t HotBlockCount);
1999e8d8bef9SDimitry Andric 
2000349cc55cSDimitry Andric   void getHotFunctions(const FuncSampleStatsMap &ProfStats,
2001349cc55cSDimitry Andric                        FuncSampleStatsMap &HotFunc,
2002e8d8bef9SDimitry Andric                        uint64_t HotThreshold) const;
2003e8d8bef9SDimitry Andric 
2004e8d8bef9SDimitry Andric   void computeHotFuncOverlap();
2005e8d8bef9SDimitry Andric 
2006e8d8bef9SDimitry Andric   /// This function updates statistics in FuncOverlap, HotBlockOverlap, and
2007e8d8bef9SDimitry Andric   /// Difference for two sample units in a matched function according to the
2008e8d8bef9SDimitry Andric   /// given match status.
2009e8d8bef9SDimitry Andric   void updateOverlapStatsForFunction(uint64_t BaseSample, uint64_t TestSample,
2010e8d8bef9SDimitry Andric                                      uint64_t HotBlockCount,
2011e8d8bef9SDimitry Andric                                      SampleOverlapStats &FuncOverlap,
2012e8d8bef9SDimitry Andric                                      double &Difference, MatchStatus Status);
2013e8d8bef9SDimitry Andric 
2014e8d8bef9SDimitry Andric   /// This function updates statistics in FuncOverlap, HotBlockOverlap, and
2015e8d8bef9SDimitry Andric   /// Difference for unmatched callees that only present in one profile in a
2016e8d8bef9SDimitry Andric   /// matched caller function.
2017e8d8bef9SDimitry Andric   void updateForUnmatchedCallee(const sampleprof::FunctionSamples &Func,
2018e8d8bef9SDimitry Andric                                 SampleOverlapStats &FuncOverlap,
2019e8d8bef9SDimitry Andric                                 double &Difference, MatchStatus Status);
2020e8d8bef9SDimitry Andric 
2021e8d8bef9SDimitry Andric   /// This function updates sample overlap statistics of an overlap function in
2022e8d8bef9SDimitry Andric   /// base and test profile. It also calculates a function-internal similarity
2023e8d8bef9SDimitry Andric   /// FIS as follows:
2024e8d8bef9SDimitry Andric   ///    For offsets i that have samples in at least one profile in this
2025e8d8bef9SDimitry Andric   ///    function A, given BS(i) returned by computeBlockSimilarity(), compute
2026e8d8bef9SDimitry Andric   ///    FIS(A) = (2.0 - sum_i(1.0 - BS(i))) / 2, ranging in [0.0f to 1.0f] with
2027e8d8bef9SDimitry Andric   ///    0.0 meaning no overlap.
2028e8d8bef9SDimitry Andric   double computeSampleFunctionInternalOverlap(
2029e8d8bef9SDimitry Andric       const sampleprof::FunctionSamples &BaseFunc,
2030e8d8bef9SDimitry Andric       const sampleprof::FunctionSamples &TestFunc,
2031e8d8bef9SDimitry Andric       SampleOverlapStats &FuncOverlap);
2032e8d8bef9SDimitry Andric 
2033e8d8bef9SDimitry Andric   /// Function-level similarity (FS) is a weighted value over function internal
2034e8d8bef9SDimitry Andric   /// similarity (FIS). This function computes a function's FS from its FIS by
2035e8d8bef9SDimitry Andric   /// applying the weight.
2036e8d8bef9SDimitry Andric   double weightForFuncSimilarity(double FuncSimilarity, uint64_t BaseFuncSample,
2037e8d8bef9SDimitry Andric                                  uint64_t TestFuncSample) const;
2038e8d8bef9SDimitry Andric 
2039e8d8bef9SDimitry Andric   /// The function-level similarity FS(A) for a function A is computed as
2040e8d8bef9SDimitry Andric   /// follows:
2041e8d8bef9SDimitry Andric   ///     Compute a function-internal similarity FIS(A) by
2042e8d8bef9SDimitry Andric   ///     computeSampleFunctionInternalOverlap(). Then, with the weight of
2043e8d8bef9SDimitry Andric   ///     function A in base profile WB(A), and the weight of function A in test
2044e8d8bef9SDimitry Andric   ///     profile WT(A), compute FS(A) = FIS(A) * (1.0 - fabs(WB(A) - WT(A)))
2045e8d8bef9SDimitry Andric   ///     ranging in [0.0f to 1.0f] with 0.0 meaning no overlap.
2046e8d8bef9SDimitry Andric   double
2047e8d8bef9SDimitry Andric   computeSampleFunctionOverlap(const sampleprof::FunctionSamples *BaseFunc,
2048e8d8bef9SDimitry Andric                                const sampleprof::FunctionSamples *TestFunc,
2049e8d8bef9SDimitry Andric                                SampleOverlapStats *FuncOverlap,
2050e8d8bef9SDimitry Andric                                uint64_t BaseFuncSample,
2051e8d8bef9SDimitry Andric                                uint64_t TestFuncSample);
2052e8d8bef9SDimitry Andric 
2053e8d8bef9SDimitry Andric   /// Profile-level similarity (PS) is a weighted aggregate over function-level
2054e8d8bef9SDimitry Andric   /// similarities (FS). This method weights the FS value by the function
2055e8d8bef9SDimitry Andric   /// weights in the base and test profiles for the aggregation.
2056e8d8bef9SDimitry Andric   double weightByImportance(double FuncSimilarity, uint64_t BaseFuncSample,
2057e8d8bef9SDimitry Andric                             uint64_t TestFuncSample) const;
2058e8d8bef9SDimitry Andric };
2059e8d8bef9SDimitry Andric } // end anonymous namespace
2060e8d8bef9SDimitry Andric 
detectZeroSampleProfile(raw_fd_ostream & OS) const2061e8d8bef9SDimitry Andric bool SampleOverlapAggregator::detectZeroSampleProfile(
2062e8d8bef9SDimitry Andric     raw_fd_ostream &OS) const {
2063e8d8bef9SDimitry Andric   bool HaveZeroSample = false;
2064e8d8bef9SDimitry Andric   if (ProfOverlap.BaseSample == 0) {
2065e8d8bef9SDimitry Andric     OS << "Sum of sample counts for profile " << BaseFilename << " is 0.\n";
2066e8d8bef9SDimitry Andric     HaveZeroSample = true;
2067e8d8bef9SDimitry Andric   }
2068e8d8bef9SDimitry Andric   if (ProfOverlap.TestSample == 0) {
2069e8d8bef9SDimitry Andric     OS << "Sum of sample counts for profile " << TestFilename << " is 0.\n";
2070e8d8bef9SDimitry Andric     HaveZeroSample = true;
2071e8d8bef9SDimitry Andric   }
2072e8d8bef9SDimitry Andric   return HaveZeroSample;
2073e8d8bef9SDimitry Andric }
2074e8d8bef9SDimitry Andric 
computeBlockSimilarity(uint64_t BaseSample,uint64_t TestSample,const SampleOverlapStats & FuncOverlap) const2075e8d8bef9SDimitry Andric double SampleOverlapAggregator::computeBlockSimilarity(
2076e8d8bef9SDimitry Andric     uint64_t BaseSample, uint64_t TestSample,
2077e8d8bef9SDimitry Andric     const SampleOverlapStats &FuncOverlap) const {
2078e8d8bef9SDimitry Andric   double BaseFrac = 0.0;
2079e8d8bef9SDimitry Andric   double TestFrac = 0.0;
2080e8d8bef9SDimitry Andric   if (FuncOverlap.BaseSample > 0)
2081e8d8bef9SDimitry Andric     BaseFrac = static_cast<double>(BaseSample) / FuncOverlap.BaseSample;
2082e8d8bef9SDimitry Andric   if (FuncOverlap.TestSample > 0)
2083e8d8bef9SDimitry Andric     TestFrac = static_cast<double>(TestSample) / FuncOverlap.TestSample;
2084e8d8bef9SDimitry Andric   return 1.0 - std::fabs(BaseFrac - TestFrac);
2085e8d8bef9SDimitry Andric }
2086e8d8bef9SDimitry Andric 
updateHotBlockOverlap(uint64_t BaseSample,uint64_t TestSample,uint64_t HotBlockCount)2087e8d8bef9SDimitry Andric void SampleOverlapAggregator::updateHotBlockOverlap(uint64_t BaseSample,
2088e8d8bef9SDimitry Andric                                                     uint64_t TestSample,
2089e8d8bef9SDimitry Andric                                                     uint64_t HotBlockCount) {
2090e8d8bef9SDimitry Andric   bool IsBaseHot = (BaseSample >= BaseHotThreshold);
2091e8d8bef9SDimitry Andric   bool IsTestHot = (TestSample >= TestHotThreshold);
2092e8d8bef9SDimitry Andric   if (!IsBaseHot && !IsTestHot)
2093e8d8bef9SDimitry Andric     return;
2094e8d8bef9SDimitry Andric 
2095e8d8bef9SDimitry Andric   HotBlockOverlap.UnionCount += HotBlockCount;
2096e8d8bef9SDimitry Andric   if (IsBaseHot)
2097e8d8bef9SDimitry Andric     HotBlockOverlap.BaseCount += HotBlockCount;
2098e8d8bef9SDimitry Andric   if (IsTestHot)
2099e8d8bef9SDimitry Andric     HotBlockOverlap.TestCount += HotBlockCount;
2100e8d8bef9SDimitry Andric   if (IsBaseHot && IsTestHot)
2101e8d8bef9SDimitry Andric     HotBlockOverlap.OverlapCount += HotBlockCount;
2102e8d8bef9SDimitry Andric }
2103e8d8bef9SDimitry Andric 
getHotFunctions(const FuncSampleStatsMap & ProfStats,FuncSampleStatsMap & HotFunc,uint64_t HotThreshold) const2104e8d8bef9SDimitry Andric void SampleOverlapAggregator::getHotFunctions(
2105349cc55cSDimitry Andric     const FuncSampleStatsMap &ProfStats, FuncSampleStatsMap &HotFunc,
2106349cc55cSDimitry Andric     uint64_t HotThreshold) const {
2107e8d8bef9SDimitry Andric   for (const auto &F : ProfStats) {
2108e8d8bef9SDimitry Andric     if (isFunctionHot(F.second, HotThreshold))
2109349cc55cSDimitry Andric       HotFunc.emplace(F.first, F.second);
2110e8d8bef9SDimitry Andric   }
2111e8d8bef9SDimitry Andric }
2112e8d8bef9SDimitry Andric 
computeHotFuncOverlap()2113e8d8bef9SDimitry Andric void SampleOverlapAggregator::computeHotFuncOverlap() {
2114349cc55cSDimitry Andric   FuncSampleStatsMap BaseHotFunc;
2115e8d8bef9SDimitry Andric   getHotFunctions(BaseStats, BaseHotFunc, BaseHotThreshold);
2116e8d8bef9SDimitry Andric   HotFuncOverlap.BaseCount = BaseHotFunc.size();
2117e8d8bef9SDimitry Andric 
2118349cc55cSDimitry Andric   FuncSampleStatsMap TestHotFunc;
2119e8d8bef9SDimitry Andric   getHotFunctions(TestStats, TestHotFunc, TestHotThreshold);
2120e8d8bef9SDimitry Andric   HotFuncOverlap.TestCount = TestHotFunc.size();
2121e8d8bef9SDimitry Andric   HotFuncOverlap.UnionCount = HotFuncOverlap.TestCount;
2122e8d8bef9SDimitry Andric 
2123e8d8bef9SDimitry Andric   for (const auto &F : BaseHotFunc) {
2124349cc55cSDimitry Andric     if (TestHotFunc.count(F.first))
2125e8d8bef9SDimitry Andric       ++HotFuncOverlap.OverlapCount;
2126e8d8bef9SDimitry Andric     else
2127e8d8bef9SDimitry Andric       ++HotFuncOverlap.UnionCount;
2128e8d8bef9SDimitry Andric   }
2129e8d8bef9SDimitry Andric }
2130e8d8bef9SDimitry Andric 
updateOverlapStatsForFunction(uint64_t BaseSample,uint64_t TestSample,uint64_t HotBlockCount,SampleOverlapStats & FuncOverlap,double & Difference,MatchStatus Status)2131e8d8bef9SDimitry Andric void SampleOverlapAggregator::updateOverlapStatsForFunction(
2132e8d8bef9SDimitry Andric     uint64_t BaseSample, uint64_t TestSample, uint64_t HotBlockCount,
2133e8d8bef9SDimitry Andric     SampleOverlapStats &FuncOverlap, double &Difference, MatchStatus Status) {
2134e8d8bef9SDimitry Andric   assert(Status != MS_None &&
2135e8d8bef9SDimitry Andric          "Match status should be updated before updating overlap statistics");
2136e8d8bef9SDimitry Andric   if (Status == MS_FirstUnique) {
2137e8d8bef9SDimitry Andric     TestSample = 0;
2138e8d8bef9SDimitry Andric     FuncOverlap.BaseUniqueSample += BaseSample;
2139e8d8bef9SDimitry Andric   } else if (Status == MS_SecondUnique) {
2140e8d8bef9SDimitry Andric     BaseSample = 0;
2141e8d8bef9SDimitry Andric     FuncOverlap.TestUniqueSample += TestSample;
2142e8d8bef9SDimitry Andric   } else {
2143e8d8bef9SDimitry Andric     ++FuncOverlap.OverlapCount;
2144e8d8bef9SDimitry Andric   }
2145e8d8bef9SDimitry Andric 
2146e8d8bef9SDimitry Andric   FuncOverlap.UnionSample += std::max(BaseSample, TestSample);
2147e8d8bef9SDimitry Andric   FuncOverlap.OverlapSample += std::min(BaseSample, TestSample);
2148e8d8bef9SDimitry Andric   Difference +=
2149e8d8bef9SDimitry Andric       1.0 - computeBlockSimilarity(BaseSample, TestSample, FuncOverlap);
2150e8d8bef9SDimitry Andric   updateHotBlockOverlap(BaseSample, TestSample, HotBlockCount);
2151e8d8bef9SDimitry Andric }
2152e8d8bef9SDimitry Andric 
updateForUnmatchedCallee(const sampleprof::FunctionSamples & Func,SampleOverlapStats & FuncOverlap,double & Difference,MatchStatus Status)2153e8d8bef9SDimitry Andric void SampleOverlapAggregator::updateForUnmatchedCallee(
2154e8d8bef9SDimitry Andric     const sampleprof::FunctionSamples &Func, SampleOverlapStats &FuncOverlap,
2155e8d8bef9SDimitry Andric     double &Difference, MatchStatus Status) {
2156e8d8bef9SDimitry Andric   assert((Status == MS_FirstUnique || Status == MS_SecondUnique) &&
2157e8d8bef9SDimitry Andric          "Status must be either of the two unmatched cases");
2158e8d8bef9SDimitry Andric   FuncSampleStats FuncStats;
2159e8d8bef9SDimitry Andric   if (Status == MS_FirstUnique) {
2160e8d8bef9SDimitry Andric     getFuncSampleStats(Func, FuncStats, BaseHotThreshold);
2161e8d8bef9SDimitry Andric     updateOverlapStatsForFunction(FuncStats.SampleSum, 0,
2162e8d8bef9SDimitry Andric                                   FuncStats.HotBlockCount, FuncOverlap,
2163e8d8bef9SDimitry Andric                                   Difference, Status);
2164e8d8bef9SDimitry Andric   } else {
2165e8d8bef9SDimitry Andric     getFuncSampleStats(Func, FuncStats, TestHotThreshold);
2166e8d8bef9SDimitry Andric     updateOverlapStatsForFunction(0, FuncStats.SampleSum,
2167e8d8bef9SDimitry Andric                                   FuncStats.HotBlockCount, FuncOverlap,
2168e8d8bef9SDimitry Andric                                   Difference, Status);
2169e8d8bef9SDimitry Andric   }
2170e8d8bef9SDimitry Andric }
2171e8d8bef9SDimitry Andric 
computeSampleFunctionInternalOverlap(const sampleprof::FunctionSamples & BaseFunc,const sampleprof::FunctionSamples & TestFunc,SampleOverlapStats & FuncOverlap)2172e8d8bef9SDimitry Andric double SampleOverlapAggregator::computeSampleFunctionInternalOverlap(
2173e8d8bef9SDimitry Andric     const sampleprof::FunctionSamples &BaseFunc,
2174e8d8bef9SDimitry Andric     const sampleprof::FunctionSamples &TestFunc,
2175e8d8bef9SDimitry Andric     SampleOverlapStats &FuncOverlap) {
2176e8d8bef9SDimitry Andric 
2177e8d8bef9SDimitry Andric   using namespace sampleprof;
2178e8d8bef9SDimitry Andric 
2179e8d8bef9SDimitry Andric   double Difference = 0;
2180e8d8bef9SDimitry Andric 
2181e8d8bef9SDimitry Andric   // Accumulate Difference for regular line/block samples in the function.
2182e8d8bef9SDimitry Andric   // We match them through sort-merge join algorithm because
2183e8d8bef9SDimitry Andric   // FunctionSamples::getBodySamples() returns a map of sample counters ordered
2184e8d8bef9SDimitry Andric   // by their offsets.
2185e8d8bef9SDimitry Andric   MatchStep<BodySampleMap::const_iterator> BlockIterStep(
2186e8d8bef9SDimitry Andric       BaseFunc.getBodySamples().cbegin(), BaseFunc.getBodySamples().cend(),
2187e8d8bef9SDimitry Andric       TestFunc.getBodySamples().cbegin(), TestFunc.getBodySamples().cend());
2188e8d8bef9SDimitry Andric   BlockIterStep.updateOneStep();
2189e8d8bef9SDimitry Andric   while (!BlockIterStep.areBothFinished()) {
2190e8d8bef9SDimitry Andric     uint64_t BaseSample =
2191e8d8bef9SDimitry Andric         BlockIterStep.isFirstFinished()
2192e8d8bef9SDimitry Andric             ? 0
2193e8d8bef9SDimitry Andric             : BlockIterStep.getFirstIter()->second.getSamples();
2194e8d8bef9SDimitry Andric     uint64_t TestSample =
2195e8d8bef9SDimitry Andric         BlockIterStep.isSecondFinished()
2196e8d8bef9SDimitry Andric             ? 0
2197e8d8bef9SDimitry Andric             : BlockIterStep.getSecondIter()->second.getSamples();
2198e8d8bef9SDimitry Andric     updateOverlapStatsForFunction(BaseSample, TestSample, 1, FuncOverlap,
2199e8d8bef9SDimitry Andric                                   Difference, BlockIterStep.getMatchStatus());
2200e8d8bef9SDimitry Andric 
2201e8d8bef9SDimitry Andric     BlockIterStep.updateOneStep();
2202e8d8bef9SDimitry Andric   }
2203e8d8bef9SDimitry Andric 
2204e8d8bef9SDimitry Andric   // Accumulate Difference for callsite lines in the function. We match
2205e8d8bef9SDimitry Andric   // them through sort-merge algorithm because
2206e8d8bef9SDimitry Andric   // FunctionSamples::getCallsiteSamples() returns a map of callsite records
2207e8d8bef9SDimitry Andric   // ordered by their offsets.
2208e8d8bef9SDimitry Andric   MatchStep<CallsiteSampleMap::const_iterator> CallsiteIterStep(
2209e8d8bef9SDimitry Andric       BaseFunc.getCallsiteSamples().cbegin(),
2210e8d8bef9SDimitry Andric       BaseFunc.getCallsiteSamples().cend(),
2211e8d8bef9SDimitry Andric       TestFunc.getCallsiteSamples().cbegin(),
2212e8d8bef9SDimitry Andric       TestFunc.getCallsiteSamples().cend());
2213e8d8bef9SDimitry Andric   CallsiteIterStep.updateOneStep();
2214e8d8bef9SDimitry Andric   while (!CallsiteIterStep.areBothFinished()) {
2215e8d8bef9SDimitry Andric     MatchStatus CallsiteStepStatus = CallsiteIterStep.getMatchStatus();
2216e8d8bef9SDimitry Andric     assert(CallsiteStepStatus != MS_None &&
2217e8d8bef9SDimitry Andric            "Match status should be updated before entering loop body");
2218e8d8bef9SDimitry Andric 
2219e8d8bef9SDimitry Andric     if (CallsiteStepStatus != MS_Match) {
2220e8d8bef9SDimitry Andric       auto Callsite = (CallsiteStepStatus == MS_FirstUnique)
2221e8d8bef9SDimitry Andric                           ? CallsiteIterStep.getFirstIter()
2222e8d8bef9SDimitry Andric                           : CallsiteIterStep.getSecondIter();
2223e8d8bef9SDimitry Andric       for (const auto &F : Callsite->second)
2224e8d8bef9SDimitry Andric         updateForUnmatchedCallee(F.second, FuncOverlap, Difference,
2225e8d8bef9SDimitry Andric                                  CallsiteStepStatus);
2226e8d8bef9SDimitry Andric     } else {
2227e8d8bef9SDimitry Andric       // There may be multiple inlinees at the same offset, so we need to try
2228e8d8bef9SDimitry Andric       // matching all of them. This match is implemented through sort-merge
2229e8d8bef9SDimitry Andric       // algorithm because callsite records at the same offset are ordered by
2230e8d8bef9SDimitry Andric       // function names.
2231e8d8bef9SDimitry Andric       MatchStep<FunctionSamplesMap::const_iterator> CalleeIterStep(
2232e8d8bef9SDimitry Andric           CallsiteIterStep.getFirstIter()->second.cbegin(),
2233e8d8bef9SDimitry Andric           CallsiteIterStep.getFirstIter()->second.cend(),
2234e8d8bef9SDimitry Andric           CallsiteIterStep.getSecondIter()->second.cbegin(),
2235e8d8bef9SDimitry Andric           CallsiteIterStep.getSecondIter()->second.cend());
2236e8d8bef9SDimitry Andric       CalleeIterStep.updateOneStep();
2237e8d8bef9SDimitry Andric       while (!CalleeIterStep.areBothFinished()) {
2238e8d8bef9SDimitry Andric         MatchStatus CalleeStepStatus = CalleeIterStep.getMatchStatus();
2239e8d8bef9SDimitry Andric         if (CalleeStepStatus != MS_Match) {
2240e8d8bef9SDimitry Andric           auto Callee = (CalleeStepStatus == MS_FirstUnique)
2241e8d8bef9SDimitry Andric                             ? CalleeIterStep.getFirstIter()
2242e8d8bef9SDimitry Andric                             : CalleeIterStep.getSecondIter();
2243e8d8bef9SDimitry Andric           updateForUnmatchedCallee(Callee->second, FuncOverlap, Difference,
2244e8d8bef9SDimitry Andric                                    CalleeStepStatus);
2245e8d8bef9SDimitry Andric         } else {
2246e8d8bef9SDimitry Andric           // An inlined function can contain other inlinees inside, so compute
2247e8d8bef9SDimitry Andric           // the Difference recursively.
2248e8d8bef9SDimitry Andric           Difference += 2.0 - 2 * computeSampleFunctionInternalOverlap(
2249e8d8bef9SDimitry Andric                                       CalleeIterStep.getFirstIter()->second,
2250e8d8bef9SDimitry Andric                                       CalleeIterStep.getSecondIter()->second,
2251e8d8bef9SDimitry Andric                                       FuncOverlap);
2252e8d8bef9SDimitry Andric         }
2253e8d8bef9SDimitry Andric         CalleeIterStep.updateOneStep();
2254e8d8bef9SDimitry Andric       }
2255e8d8bef9SDimitry Andric     }
2256e8d8bef9SDimitry Andric     CallsiteIterStep.updateOneStep();
2257e8d8bef9SDimitry Andric   }
2258e8d8bef9SDimitry Andric 
2259e8d8bef9SDimitry Andric   // Difference reflects the total differences of line/block samples in this
2260e8d8bef9SDimitry Andric   // function and ranges in [0.0f to 2.0f]. Take (2.0 - Difference) / 2 to
2261e8d8bef9SDimitry Andric   // reflect the similarity between function profiles in [0.0f to 1.0f].
2262e8d8bef9SDimitry Andric   return (2.0 - Difference) / 2;
2263e8d8bef9SDimitry Andric }
2264e8d8bef9SDimitry Andric 
weightForFuncSimilarity(double FuncInternalSimilarity,uint64_t BaseFuncSample,uint64_t TestFuncSample) const2265e8d8bef9SDimitry Andric double SampleOverlapAggregator::weightForFuncSimilarity(
2266e8d8bef9SDimitry Andric     double FuncInternalSimilarity, uint64_t BaseFuncSample,
2267e8d8bef9SDimitry Andric     uint64_t TestFuncSample) const {
2268e8d8bef9SDimitry Andric   // Compute the weight as the distance between the function weights in two
2269e8d8bef9SDimitry Andric   // profiles.
2270e8d8bef9SDimitry Andric   double BaseFrac = 0.0;
2271e8d8bef9SDimitry Andric   double TestFrac = 0.0;
2272e8d8bef9SDimitry Andric   assert(ProfOverlap.BaseSample > 0 &&
2273e8d8bef9SDimitry Andric          "Total samples in base profile should be greater than 0");
2274e8d8bef9SDimitry Andric   BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample;
2275e8d8bef9SDimitry Andric   assert(ProfOverlap.TestSample > 0 &&
2276e8d8bef9SDimitry Andric          "Total samples in test profile should be greater than 0");
2277e8d8bef9SDimitry Andric   TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample;
2278e8d8bef9SDimitry Andric   double WeightDistance = std::fabs(BaseFrac - TestFrac);
2279e8d8bef9SDimitry Andric 
2280e8d8bef9SDimitry Andric   // Take WeightDistance into the similarity.
2281e8d8bef9SDimitry Andric   return FuncInternalSimilarity * (1 - WeightDistance);
2282e8d8bef9SDimitry Andric }
2283e8d8bef9SDimitry Andric 
2284e8d8bef9SDimitry Andric double
weightByImportance(double FuncSimilarity,uint64_t BaseFuncSample,uint64_t TestFuncSample) const2285e8d8bef9SDimitry Andric SampleOverlapAggregator::weightByImportance(double FuncSimilarity,
2286e8d8bef9SDimitry Andric                                             uint64_t BaseFuncSample,
2287e8d8bef9SDimitry Andric                                             uint64_t TestFuncSample) const {
2288e8d8bef9SDimitry Andric 
2289e8d8bef9SDimitry Andric   double BaseFrac = 0.0;
2290e8d8bef9SDimitry Andric   double TestFrac = 0.0;
2291e8d8bef9SDimitry Andric   assert(ProfOverlap.BaseSample > 0 &&
2292e8d8bef9SDimitry Andric          "Total samples in base profile should be greater than 0");
2293e8d8bef9SDimitry Andric   BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample / 2.0;
2294e8d8bef9SDimitry Andric   assert(ProfOverlap.TestSample > 0 &&
2295e8d8bef9SDimitry Andric          "Total samples in test profile should be greater than 0");
2296e8d8bef9SDimitry Andric   TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample / 2.0;
2297e8d8bef9SDimitry Andric   return FuncSimilarity * (BaseFrac + TestFrac);
2298e8d8bef9SDimitry Andric }
2299e8d8bef9SDimitry Andric 
computeSampleFunctionOverlap(const sampleprof::FunctionSamples * BaseFunc,const sampleprof::FunctionSamples * TestFunc,SampleOverlapStats * FuncOverlap,uint64_t BaseFuncSample,uint64_t TestFuncSample)2300e8d8bef9SDimitry Andric double SampleOverlapAggregator::computeSampleFunctionOverlap(
2301e8d8bef9SDimitry Andric     const sampleprof::FunctionSamples *BaseFunc,
2302e8d8bef9SDimitry Andric     const sampleprof::FunctionSamples *TestFunc,
2303e8d8bef9SDimitry Andric     SampleOverlapStats *FuncOverlap, uint64_t BaseFuncSample,
2304e8d8bef9SDimitry Andric     uint64_t TestFuncSample) {
2305e8d8bef9SDimitry Andric   // Default function internal similarity before weighted, meaning two functions
2306e8d8bef9SDimitry Andric   // has no overlap.
2307e8d8bef9SDimitry Andric   const double DefaultFuncInternalSimilarity = 0;
2308e8d8bef9SDimitry Andric   double FuncSimilarity;
2309e8d8bef9SDimitry Andric   double FuncInternalSimilarity;
2310e8d8bef9SDimitry Andric 
2311e8d8bef9SDimitry Andric   // If BaseFunc or TestFunc is nullptr, it means the functions do not overlap.
2312e8d8bef9SDimitry Andric   // In this case, we use DefaultFuncInternalSimilarity as the function internal
2313e8d8bef9SDimitry Andric   // similarity.
2314e8d8bef9SDimitry Andric   if (!BaseFunc || !TestFunc) {
2315e8d8bef9SDimitry Andric     FuncInternalSimilarity = DefaultFuncInternalSimilarity;
2316e8d8bef9SDimitry Andric   } else {
2317e8d8bef9SDimitry Andric     assert(FuncOverlap != nullptr &&
2318e8d8bef9SDimitry Andric            "FuncOverlap should be provided in this case");
2319e8d8bef9SDimitry Andric     FuncInternalSimilarity = computeSampleFunctionInternalOverlap(
2320e8d8bef9SDimitry Andric         *BaseFunc, *TestFunc, *FuncOverlap);
2321e8d8bef9SDimitry Andric     // Now, FuncInternalSimilarity may be a little less than 0 due to
2322e8d8bef9SDimitry Andric     // imprecision of floating point accumulations. Make it zero if the
2323e8d8bef9SDimitry Andric     // difference is below Epsilon.
2324e8d8bef9SDimitry Andric     FuncInternalSimilarity = (std::fabs(FuncInternalSimilarity - 0) < Epsilon)
2325e8d8bef9SDimitry Andric                                  ? 0
2326e8d8bef9SDimitry Andric                                  : FuncInternalSimilarity;
2327e8d8bef9SDimitry Andric   }
2328e8d8bef9SDimitry Andric   FuncSimilarity = weightForFuncSimilarity(FuncInternalSimilarity,
2329e8d8bef9SDimitry Andric                                            BaseFuncSample, TestFuncSample);
2330e8d8bef9SDimitry Andric   return FuncSimilarity;
2331e8d8bef9SDimitry Andric }
2332e8d8bef9SDimitry Andric 
computeSampleProfileOverlap(raw_fd_ostream & OS)2333e8d8bef9SDimitry Andric void SampleOverlapAggregator::computeSampleProfileOverlap(raw_fd_ostream &OS) {
2334e8d8bef9SDimitry Andric   using namespace sampleprof;
2335e8d8bef9SDimitry Andric 
2336349cc55cSDimitry Andric   std::unordered_map<SampleContext, const FunctionSamples *,
2337349cc55cSDimitry Andric                      SampleContext::Hash>
2338349cc55cSDimitry Andric       BaseFuncProf;
2339e8d8bef9SDimitry Andric   const auto &BaseProfiles = BaseReader->getProfiles();
2340e8d8bef9SDimitry Andric   for (const auto &BaseFunc : BaseProfiles) {
2341349cc55cSDimitry Andric     BaseFuncProf.emplace(BaseFunc.second.getContext(), &(BaseFunc.second));
2342e8d8bef9SDimitry Andric   }
2343e8d8bef9SDimitry Andric   ProfOverlap.UnionCount = BaseFuncProf.size();
2344e8d8bef9SDimitry Andric 
2345e8d8bef9SDimitry Andric   const auto &TestProfiles = TestReader->getProfiles();
2346e8d8bef9SDimitry Andric   for (const auto &TestFunc : TestProfiles) {
2347e8d8bef9SDimitry Andric     SampleOverlapStats FuncOverlap;
2348349cc55cSDimitry Andric     FuncOverlap.TestName = TestFunc.second.getContext();
2349e8d8bef9SDimitry Andric     assert(TestStats.count(FuncOverlap.TestName) &&
2350e8d8bef9SDimitry Andric            "TestStats should have records for all functions in test profile "
2351e8d8bef9SDimitry Andric            "except inlinees");
2352e8d8bef9SDimitry Andric     FuncOverlap.TestSample = TestStats[FuncOverlap.TestName].SampleSum;
2353e8d8bef9SDimitry Andric 
2354349cc55cSDimitry Andric     bool Matched = false;
2355e8d8bef9SDimitry Andric     const auto Match = BaseFuncProf.find(FuncOverlap.TestName);
2356e8d8bef9SDimitry Andric     if (Match == BaseFuncProf.end()) {
2357e8d8bef9SDimitry Andric       const FuncSampleStats &FuncStats = TestStats[FuncOverlap.TestName];
2358e8d8bef9SDimitry Andric       ++ProfOverlap.TestUniqueCount;
2359e8d8bef9SDimitry Andric       ProfOverlap.TestUniqueSample += FuncStats.SampleSum;
2360e8d8bef9SDimitry Andric       FuncOverlap.TestUniqueSample = FuncStats.SampleSum;
2361e8d8bef9SDimitry Andric 
2362e8d8bef9SDimitry Andric       updateHotBlockOverlap(0, FuncStats.SampleSum, FuncStats.HotBlockCount);
2363e8d8bef9SDimitry Andric 
2364e8d8bef9SDimitry Andric       double FuncSimilarity = computeSampleFunctionOverlap(
2365e8d8bef9SDimitry Andric           nullptr, nullptr, nullptr, 0, FuncStats.SampleSum);
2366e8d8bef9SDimitry Andric       ProfOverlap.Similarity +=
2367e8d8bef9SDimitry Andric           weightByImportance(FuncSimilarity, 0, FuncStats.SampleSum);
2368e8d8bef9SDimitry Andric 
2369e8d8bef9SDimitry Andric       ++ProfOverlap.UnionCount;
2370e8d8bef9SDimitry Andric       ProfOverlap.UnionSample += FuncStats.SampleSum;
2371e8d8bef9SDimitry Andric     } else {
2372e8d8bef9SDimitry Andric       ++ProfOverlap.OverlapCount;
2373e8d8bef9SDimitry Andric 
2374e8d8bef9SDimitry Andric       // Two functions match with each other. Compute function-level overlap and
2375e8d8bef9SDimitry Andric       // aggregate them into profile-level overlap.
2376349cc55cSDimitry Andric       FuncOverlap.BaseName = Match->second->getContext();
2377e8d8bef9SDimitry Andric       assert(BaseStats.count(FuncOverlap.BaseName) &&
2378e8d8bef9SDimitry Andric              "BaseStats should have records for all functions in base profile "
2379e8d8bef9SDimitry Andric              "except inlinees");
2380e8d8bef9SDimitry Andric       FuncOverlap.BaseSample = BaseStats[FuncOverlap.BaseName].SampleSum;
2381e8d8bef9SDimitry Andric 
2382e8d8bef9SDimitry Andric       FuncOverlap.Similarity = computeSampleFunctionOverlap(
2383e8d8bef9SDimitry Andric           Match->second, &TestFunc.second, &FuncOverlap, FuncOverlap.BaseSample,
2384e8d8bef9SDimitry Andric           FuncOverlap.TestSample);
2385e8d8bef9SDimitry Andric       ProfOverlap.Similarity +=
2386e8d8bef9SDimitry Andric           weightByImportance(FuncOverlap.Similarity, FuncOverlap.BaseSample,
2387e8d8bef9SDimitry Andric                              FuncOverlap.TestSample);
2388e8d8bef9SDimitry Andric       ProfOverlap.OverlapSample += FuncOverlap.OverlapSample;
2389e8d8bef9SDimitry Andric       ProfOverlap.UnionSample += FuncOverlap.UnionSample;
2390e8d8bef9SDimitry Andric 
2391e8d8bef9SDimitry Andric       // Accumulate the percentage of base unique and test unique samples into
2392e8d8bef9SDimitry Andric       // ProfOverlap.
2393e8d8bef9SDimitry Andric       ProfOverlap.BaseUniqueSample += FuncOverlap.BaseUniqueSample;
2394e8d8bef9SDimitry Andric       ProfOverlap.TestUniqueSample += FuncOverlap.TestUniqueSample;
2395e8d8bef9SDimitry Andric 
2396e8d8bef9SDimitry Andric       // Remove matched base functions for later reporting functions not found
2397e8d8bef9SDimitry Andric       // in test profile.
2398e8d8bef9SDimitry Andric       BaseFuncProf.erase(Match);
2399349cc55cSDimitry Andric       Matched = true;
2400e8d8bef9SDimitry Andric     }
2401e8d8bef9SDimitry Andric 
2402e8d8bef9SDimitry Andric     // Print function-level similarity information if specified by options.
2403e8d8bef9SDimitry Andric     assert(TestStats.count(FuncOverlap.TestName) &&
2404e8d8bef9SDimitry Andric            "TestStats should have records for all functions in test profile "
2405e8d8bef9SDimitry Andric            "except inlinees");
2406e8d8bef9SDimitry Andric     if (TestStats[FuncOverlap.TestName].MaxSample >= FuncFilter.ValueCutoff ||
2407349cc55cSDimitry Andric         (Matched && FuncOverlap.Similarity < LowSimilarityThreshold) ||
2408349cc55cSDimitry Andric         (Matched && !FuncFilter.NameFilter.empty() &&
2409349cc55cSDimitry Andric          FuncOverlap.BaseName.toString().find(FuncFilter.NameFilter) !=
2410349cc55cSDimitry Andric              std::string::npos)) {
2411e8d8bef9SDimitry Andric       assert(ProfOverlap.BaseSample > 0 &&
2412e8d8bef9SDimitry Andric              "Total samples in base profile should be greater than 0");
2413e8d8bef9SDimitry Andric       FuncOverlap.BaseWeight =
2414e8d8bef9SDimitry Andric           static_cast<double>(FuncOverlap.BaseSample) / ProfOverlap.BaseSample;
2415e8d8bef9SDimitry Andric       assert(ProfOverlap.TestSample > 0 &&
2416e8d8bef9SDimitry Andric              "Total samples in test profile should be greater than 0");
2417e8d8bef9SDimitry Andric       FuncOverlap.TestWeight =
2418e8d8bef9SDimitry Andric           static_cast<double>(FuncOverlap.TestSample) / ProfOverlap.TestSample;
2419e8d8bef9SDimitry Andric       FuncSimilarityDump.emplace(FuncOverlap.BaseWeight, FuncOverlap);
2420e8d8bef9SDimitry Andric     }
2421e8d8bef9SDimitry Andric   }
2422e8d8bef9SDimitry Andric 
2423e8d8bef9SDimitry Andric   // Traverse through functions in base profile but not in test profile.
2424e8d8bef9SDimitry Andric   for (const auto &F : BaseFuncProf) {
2425349cc55cSDimitry Andric     assert(BaseStats.count(F.second->getContext()) &&
2426e8d8bef9SDimitry Andric            "BaseStats should have records for all functions in base profile "
2427e8d8bef9SDimitry Andric            "except inlinees");
2428349cc55cSDimitry Andric     const FuncSampleStats &FuncStats = BaseStats[F.second->getContext()];
2429e8d8bef9SDimitry Andric     ++ProfOverlap.BaseUniqueCount;
2430e8d8bef9SDimitry Andric     ProfOverlap.BaseUniqueSample += FuncStats.SampleSum;
2431e8d8bef9SDimitry Andric 
2432e8d8bef9SDimitry Andric     updateHotBlockOverlap(FuncStats.SampleSum, 0, FuncStats.HotBlockCount);
2433e8d8bef9SDimitry Andric 
2434e8d8bef9SDimitry Andric     double FuncSimilarity = computeSampleFunctionOverlap(
2435e8d8bef9SDimitry Andric         nullptr, nullptr, nullptr, FuncStats.SampleSum, 0);
2436e8d8bef9SDimitry Andric     ProfOverlap.Similarity +=
2437e8d8bef9SDimitry Andric         weightByImportance(FuncSimilarity, FuncStats.SampleSum, 0);
2438e8d8bef9SDimitry Andric 
2439e8d8bef9SDimitry Andric     ProfOverlap.UnionSample += FuncStats.SampleSum;
2440e8d8bef9SDimitry Andric   }
2441e8d8bef9SDimitry Andric 
2442e8d8bef9SDimitry Andric   // Now, ProfSimilarity may be a little greater than 1 due to imprecision
2443e8d8bef9SDimitry Andric   // of floating point accumulations. Make it 1.0 if the difference is below
2444e8d8bef9SDimitry Andric   // Epsilon.
2445e8d8bef9SDimitry Andric   ProfOverlap.Similarity = (std::fabs(ProfOverlap.Similarity - 1) < Epsilon)
2446e8d8bef9SDimitry Andric                                ? 1
2447e8d8bef9SDimitry Andric                                : ProfOverlap.Similarity;
2448e8d8bef9SDimitry Andric 
2449e8d8bef9SDimitry Andric   computeHotFuncOverlap();
2450e8d8bef9SDimitry Andric }
2451e8d8bef9SDimitry Andric 
initializeSampleProfileOverlap()2452e8d8bef9SDimitry Andric void SampleOverlapAggregator::initializeSampleProfileOverlap() {
2453e8d8bef9SDimitry Andric   const auto &BaseProf = BaseReader->getProfiles();
2454e8d8bef9SDimitry Andric   for (const auto &I : BaseProf) {
2455e8d8bef9SDimitry Andric     ++ProfOverlap.BaseCount;
2456e8d8bef9SDimitry Andric     FuncSampleStats FuncStats;
2457e8d8bef9SDimitry Andric     getFuncSampleStats(I.second, FuncStats, BaseHotThreshold);
2458e8d8bef9SDimitry Andric     ProfOverlap.BaseSample += FuncStats.SampleSum;
2459349cc55cSDimitry Andric     BaseStats.emplace(I.second.getContext(), FuncStats);
2460e8d8bef9SDimitry Andric   }
2461e8d8bef9SDimitry Andric 
2462e8d8bef9SDimitry Andric   const auto &TestProf = TestReader->getProfiles();
2463e8d8bef9SDimitry Andric   for (const auto &I : TestProf) {
2464e8d8bef9SDimitry Andric     ++ProfOverlap.TestCount;
2465e8d8bef9SDimitry Andric     FuncSampleStats FuncStats;
2466e8d8bef9SDimitry Andric     getFuncSampleStats(I.second, FuncStats, TestHotThreshold);
2467e8d8bef9SDimitry Andric     ProfOverlap.TestSample += FuncStats.SampleSum;
2468349cc55cSDimitry Andric     TestStats.emplace(I.second.getContext(), FuncStats);
2469e8d8bef9SDimitry Andric   }
2470e8d8bef9SDimitry Andric 
2471e8d8bef9SDimitry Andric   ProfOverlap.BaseName = StringRef(BaseFilename);
2472e8d8bef9SDimitry Andric   ProfOverlap.TestName = StringRef(TestFilename);
2473e8d8bef9SDimitry Andric }
2474e8d8bef9SDimitry Andric 
dumpFuncSimilarity(raw_fd_ostream & OS) const2475e8d8bef9SDimitry Andric void SampleOverlapAggregator::dumpFuncSimilarity(raw_fd_ostream &OS) const {
2476e8d8bef9SDimitry Andric   using namespace sampleprof;
2477e8d8bef9SDimitry Andric 
2478e8d8bef9SDimitry Andric   if (FuncSimilarityDump.empty())
2479e8d8bef9SDimitry Andric     return;
2480e8d8bef9SDimitry Andric 
2481e8d8bef9SDimitry Andric   formatted_raw_ostream FOS(OS);
2482e8d8bef9SDimitry Andric   FOS << "Function-level details:\n";
2483e8d8bef9SDimitry Andric   FOS << "Base weight";
2484e8d8bef9SDimitry Andric   FOS.PadToColumn(TestWeightCol);
2485e8d8bef9SDimitry Andric   FOS << "Test weight";
2486e8d8bef9SDimitry Andric   FOS.PadToColumn(SimilarityCol);
2487e8d8bef9SDimitry Andric   FOS << "Similarity";
2488e8d8bef9SDimitry Andric   FOS.PadToColumn(OverlapCol);
2489e8d8bef9SDimitry Andric   FOS << "Overlap";
2490e8d8bef9SDimitry Andric   FOS.PadToColumn(BaseUniqueCol);
2491e8d8bef9SDimitry Andric   FOS << "Base unique";
2492e8d8bef9SDimitry Andric   FOS.PadToColumn(TestUniqueCol);
2493e8d8bef9SDimitry Andric   FOS << "Test unique";
2494e8d8bef9SDimitry Andric   FOS.PadToColumn(BaseSampleCol);
2495e8d8bef9SDimitry Andric   FOS << "Base samples";
2496e8d8bef9SDimitry Andric   FOS.PadToColumn(TestSampleCol);
2497e8d8bef9SDimitry Andric   FOS << "Test samples";
2498e8d8bef9SDimitry Andric   FOS.PadToColumn(FuncNameCol);
2499e8d8bef9SDimitry Andric   FOS << "Function name\n";
2500e8d8bef9SDimitry Andric   for (const auto &F : FuncSimilarityDump) {
2501e8d8bef9SDimitry Andric     double OverlapPercent =
2502e8d8bef9SDimitry Andric         F.second.UnionSample > 0
2503e8d8bef9SDimitry Andric             ? static_cast<double>(F.second.OverlapSample) / F.second.UnionSample
2504e8d8bef9SDimitry Andric             : 0;
2505e8d8bef9SDimitry Andric     double BaseUniquePercent =
2506e8d8bef9SDimitry Andric         F.second.BaseSample > 0
2507e8d8bef9SDimitry Andric             ? static_cast<double>(F.second.BaseUniqueSample) /
2508e8d8bef9SDimitry Andric                   F.second.BaseSample
2509e8d8bef9SDimitry Andric             : 0;
2510e8d8bef9SDimitry Andric     double TestUniquePercent =
2511e8d8bef9SDimitry Andric         F.second.TestSample > 0
2512e8d8bef9SDimitry Andric             ? static_cast<double>(F.second.TestUniqueSample) /
2513e8d8bef9SDimitry Andric                   F.second.TestSample
2514e8d8bef9SDimitry Andric             : 0;
2515e8d8bef9SDimitry Andric 
2516e8d8bef9SDimitry Andric     FOS << format("%.2f%%", F.second.BaseWeight * 100);
2517e8d8bef9SDimitry Andric     FOS.PadToColumn(TestWeightCol);
2518e8d8bef9SDimitry Andric     FOS << format("%.2f%%", F.second.TestWeight * 100);
2519e8d8bef9SDimitry Andric     FOS.PadToColumn(SimilarityCol);
2520e8d8bef9SDimitry Andric     FOS << format("%.2f%%", F.second.Similarity * 100);
2521e8d8bef9SDimitry Andric     FOS.PadToColumn(OverlapCol);
2522e8d8bef9SDimitry Andric     FOS << format("%.2f%%", OverlapPercent * 100);
2523e8d8bef9SDimitry Andric     FOS.PadToColumn(BaseUniqueCol);
2524e8d8bef9SDimitry Andric     FOS << format("%.2f%%", BaseUniquePercent * 100);
2525e8d8bef9SDimitry Andric     FOS.PadToColumn(TestUniqueCol);
2526e8d8bef9SDimitry Andric     FOS << format("%.2f%%", TestUniquePercent * 100);
2527e8d8bef9SDimitry Andric     FOS.PadToColumn(BaseSampleCol);
2528e8d8bef9SDimitry Andric     FOS << F.second.BaseSample;
2529e8d8bef9SDimitry Andric     FOS.PadToColumn(TestSampleCol);
2530e8d8bef9SDimitry Andric     FOS << F.second.TestSample;
2531e8d8bef9SDimitry Andric     FOS.PadToColumn(FuncNameCol);
2532349cc55cSDimitry Andric     FOS << F.second.TestName.toString() << "\n";
2533e8d8bef9SDimitry Andric   }
2534e8d8bef9SDimitry Andric }
2535e8d8bef9SDimitry Andric 
dumpProgramSummary(raw_fd_ostream & OS) const2536e8d8bef9SDimitry Andric void SampleOverlapAggregator::dumpProgramSummary(raw_fd_ostream &OS) const {
2537349cc55cSDimitry Andric   OS << "Profile overlap infomation for base_profile: "
2538349cc55cSDimitry Andric      << ProfOverlap.BaseName.toString()
2539349cc55cSDimitry Andric      << " and test_profile: " << ProfOverlap.TestName.toString()
2540349cc55cSDimitry Andric      << "\nProgram level:\n";
2541e8d8bef9SDimitry Andric 
2542e8d8bef9SDimitry Andric   OS << "  Whole program profile similarity: "
2543e8d8bef9SDimitry Andric      << format("%.3f%%", ProfOverlap.Similarity * 100) << "\n";
2544e8d8bef9SDimitry Andric 
2545e8d8bef9SDimitry Andric   assert(ProfOverlap.UnionSample > 0 &&
2546e8d8bef9SDimitry Andric          "Total samples in two profile should be greater than 0");
2547e8d8bef9SDimitry Andric   double OverlapPercent =
2548e8d8bef9SDimitry Andric       static_cast<double>(ProfOverlap.OverlapSample) / ProfOverlap.UnionSample;
2549e8d8bef9SDimitry Andric   assert(ProfOverlap.BaseSample > 0 &&
2550e8d8bef9SDimitry Andric          "Total samples in base profile should be greater than 0");
2551e8d8bef9SDimitry Andric   double BaseUniquePercent = static_cast<double>(ProfOverlap.BaseUniqueSample) /
2552e8d8bef9SDimitry Andric                              ProfOverlap.BaseSample;
2553e8d8bef9SDimitry Andric   assert(ProfOverlap.TestSample > 0 &&
2554e8d8bef9SDimitry Andric          "Total samples in test profile should be greater than 0");
2555e8d8bef9SDimitry Andric   double TestUniquePercent = static_cast<double>(ProfOverlap.TestUniqueSample) /
2556e8d8bef9SDimitry Andric                              ProfOverlap.TestSample;
2557e8d8bef9SDimitry Andric 
2558e8d8bef9SDimitry Andric   OS << "  Whole program sample overlap: "
2559e8d8bef9SDimitry Andric      << format("%.3f%%", OverlapPercent * 100) << "\n";
2560e8d8bef9SDimitry Andric   OS << "    percentage of samples unique in base profile: "
2561e8d8bef9SDimitry Andric      << format("%.3f%%", BaseUniquePercent * 100) << "\n";
2562e8d8bef9SDimitry Andric   OS << "    percentage of samples unique in test profile: "
2563e8d8bef9SDimitry Andric      << format("%.3f%%", TestUniquePercent * 100) << "\n";
2564e8d8bef9SDimitry Andric   OS << "    total samples in base profile: " << ProfOverlap.BaseSample << "\n"
2565e8d8bef9SDimitry Andric      << "    total samples in test profile: " << ProfOverlap.TestSample << "\n";
2566e8d8bef9SDimitry Andric 
2567e8d8bef9SDimitry Andric   assert(ProfOverlap.UnionCount > 0 &&
2568e8d8bef9SDimitry Andric          "There should be at least one function in two input profiles");
2569e8d8bef9SDimitry Andric   double FuncOverlapPercent =
2570e8d8bef9SDimitry Andric       static_cast<double>(ProfOverlap.OverlapCount) / ProfOverlap.UnionCount;
2571e8d8bef9SDimitry Andric   OS << "  Function overlap: " << format("%.3f%%", FuncOverlapPercent * 100)
2572e8d8bef9SDimitry Andric      << "\n";
2573e8d8bef9SDimitry Andric   OS << "    overlap functions: " << ProfOverlap.OverlapCount << "\n";
2574e8d8bef9SDimitry Andric   OS << "    functions unique in base profile: " << ProfOverlap.BaseUniqueCount
2575e8d8bef9SDimitry Andric      << "\n";
2576e8d8bef9SDimitry Andric   OS << "    functions unique in test profile: " << ProfOverlap.TestUniqueCount
2577e8d8bef9SDimitry Andric      << "\n";
2578e8d8bef9SDimitry Andric }
2579e8d8bef9SDimitry Andric 
dumpHotFuncAndBlockOverlap(raw_fd_ostream & OS) const2580e8d8bef9SDimitry Andric void SampleOverlapAggregator::dumpHotFuncAndBlockOverlap(
2581e8d8bef9SDimitry Andric     raw_fd_ostream &OS) const {
2582e8d8bef9SDimitry Andric   assert(HotFuncOverlap.UnionCount > 0 &&
2583e8d8bef9SDimitry Andric          "There should be at least one hot function in two input profiles");
2584e8d8bef9SDimitry Andric   OS << "  Hot-function overlap: "
2585e8d8bef9SDimitry Andric      << format("%.3f%%", static_cast<double>(HotFuncOverlap.OverlapCount) /
2586e8d8bef9SDimitry Andric                              HotFuncOverlap.UnionCount * 100)
2587e8d8bef9SDimitry Andric      << "\n";
2588e8d8bef9SDimitry Andric   OS << "    overlap hot functions: " << HotFuncOverlap.OverlapCount << "\n";
2589e8d8bef9SDimitry Andric   OS << "    hot functions unique in base profile: "
2590e8d8bef9SDimitry Andric      << HotFuncOverlap.BaseCount - HotFuncOverlap.OverlapCount << "\n";
2591e8d8bef9SDimitry Andric   OS << "    hot functions unique in test profile: "
2592e8d8bef9SDimitry Andric      << HotFuncOverlap.TestCount - HotFuncOverlap.OverlapCount << "\n";
2593e8d8bef9SDimitry Andric 
2594e8d8bef9SDimitry Andric   assert(HotBlockOverlap.UnionCount > 0 &&
2595e8d8bef9SDimitry Andric          "There should be at least one hot block in two input profiles");
2596e8d8bef9SDimitry Andric   OS << "  Hot-block overlap: "
2597e8d8bef9SDimitry Andric      << format("%.3f%%", static_cast<double>(HotBlockOverlap.OverlapCount) /
2598e8d8bef9SDimitry Andric                              HotBlockOverlap.UnionCount * 100)
2599e8d8bef9SDimitry Andric      << "\n";
2600e8d8bef9SDimitry Andric   OS << "    overlap hot blocks: " << HotBlockOverlap.OverlapCount << "\n";
2601e8d8bef9SDimitry Andric   OS << "    hot blocks unique in base profile: "
2602e8d8bef9SDimitry Andric      << HotBlockOverlap.BaseCount - HotBlockOverlap.OverlapCount << "\n";
2603e8d8bef9SDimitry Andric   OS << "    hot blocks unique in test profile: "
2604e8d8bef9SDimitry Andric      << HotBlockOverlap.TestCount - HotBlockOverlap.OverlapCount << "\n";
2605e8d8bef9SDimitry Andric }
2606e8d8bef9SDimitry Andric 
loadProfiles()2607e8d8bef9SDimitry Andric std::error_code SampleOverlapAggregator::loadProfiles() {
2608e8d8bef9SDimitry Andric   using namespace sampleprof;
2609e8d8bef9SDimitry Andric 
2610e8d8bef9SDimitry Andric   LLVMContext Context;
261106c3fb27SDimitry Andric   auto FS = vfs::getRealFileSystem();
261206c3fb27SDimitry Andric   auto BaseReaderOrErr = SampleProfileReader::create(BaseFilename, Context, *FS,
2613fe6060f1SDimitry Andric                                                      FSDiscriminatorPassOption);
2614e8d8bef9SDimitry Andric   if (std::error_code EC = BaseReaderOrErr.getError())
2615e8d8bef9SDimitry Andric     exitWithErrorCode(EC, BaseFilename);
2616e8d8bef9SDimitry Andric 
261706c3fb27SDimitry Andric   auto TestReaderOrErr = SampleProfileReader::create(TestFilename, Context, *FS,
2618fe6060f1SDimitry Andric                                                      FSDiscriminatorPassOption);
2619e8d8bef9SDimitry Andric   if (std::error_code EC = TestReaderOrErr.getError())
2620e8d8bef9SDimitry Andric     exitWithErrorCode(EC, TestFilename);
2621e8d8bef9SDimitry Andric 
2622e8d8bef9SDimitry Andric   BaseReader = std::move(BaseReaderOrErr.get());
2623e8d8bef9SDimitry Andric   TestReader = std::move(TestReaderOrErr.get());
2624e8d8bef9SDimitry Andric 
2625e8d8bef9SDimitry Andric   if (std::error_code EC = BaseReader->read())
2626e8d8bef9SDimitry Andric     exitWithErrorCode(EC, BaseFilename);
2627e8d8bef9SDimitry Andric   if (std::error_code EC = TestReader->read())
2628e8d8bef9SDimitry Andric     exitWithErrorCode(EC, TestFilename);
2629e8d8bef9SDimitry Andric   if (BaseReader->profileIsProbeBased() != TestReader->profileIsProbeBased())
2630e8d8bef9SDimitry Andric     exitWithError(
2631e8d8bef9SDimitry Andric         "cannot compare probe-based profile with non-probe-based profile");
263281ad6265SDimitry Andric   if (BaseReader->profileIsCS() != TestReader->profileIsCS())
2633fe6060f1SDimitry Andric     exitWithError("cannot compare CS profile with non-CS profile");
2634e8d8bef9SDimitry Andric 
2635e8d8bef9SDimitry Andric   // Load BaseHotThreshold and TestHotThreshold as 99-percentile threshold in
2636e8d8bef9SDimitry Andric   // profile summary.
2637e8d8bef9SDimitry Andric   ProfileSummary &BasePS = BaseReader->getSummary();
2638e8d8bef9SDimitry Andric   ProfileSummary &TestPS = TestReader->getSummary();
2639349cc55cSDimitry Andric   BaseHotThreshold =
2640349cc55cSDimitry Andric       ProfileSummaryBuilder::getHotCountThreshold(BasePS.getDetailedSummary());
2641349cc55cSDimitry Andric   TestHotThreshold =
2642349cc55cSDimitry Andric       ProfileSummaryBuilder::getHotCountThreshold(TestPS.getDetailedSummary());
2643349cc55cSDimitry Andric 
2644e8d8bef9SDimitry Andric   return std::error_code();
2645e8d8bef9SDimitry Andric }
2646e8d8bef9SDimitry Andric 
overlapSampleProfile(const std::string & BaseFilename,const std::string & TestFilename,const OverlapFuncFilters & FuncFilter,uint64_t SimilarityCutoff,raw_fd_ostream & OS)2647e8d8bef9SDimitry Andric void overlapSampleProfile(const std::string &BaseFilename,
2648e8d8bef9SDimitry Andric                           const std::string &TestFilename,
2649e8d8bef9SDimitry Andric                           const OverlapFuncFilters &FuncFilter,
2650e8d8bef9SDimitry Andric                           uint64_t SimilarityCutoff, raw_fd_ostream &OS) {
2651e8d8bef9SDimitry Andric   using namespace sampleprof;
2652e8d8bef9SDimitry Andric 
2653e8d8bef9SDimitry Andric   // We use 0.000005 to initialize OverlapAggr.Epsilon because the final metrics
2654e8d8bef9SDimitry Andric   // report 2--3 places after decimal point in percentage numbers.
2655e8d8bef9SDimitry Andric   SampleOverlapAggregator OverlapAggr(
2656e8d8bef9SDimitry Andric       BaseFilename, TestFilename,
2657e8d8bef9SDimitry Andric       static_cast<double>(SimilarityCutoff) / 1000000, 0.000005, FuncFilter);
2658e8d8bef9SDimitry Andric   if (std::error_code EC = OverlapAggr.loadProfiles())
2659e8d8bef9SDimitry Andric     exitWithErrorCode(EC);
2660e8d8bef9SDimitry Andric 
2661e8d8bef9SDimitry Andric   OverlapAggr.initializeSampleProfileOverlap();
2662e8d8bef9SDimitry Andric   if (OverlapAggr.detectZeroSampleProfile(OS))
2663e8d8bef9SDimitry Andric     return;
2664e8d8bef9SDimitry Andric 
2665e8d8bef9SDimitry Andric   OverlapAggr.computeSampleProfileOverlap(OS);
2666e8d8bef9SDimitry Andric 
2667e8d8bef9SDimitry Andric   OverlapAggr.dumpProgramSummary(OS);
2668e8d8bef9SDimitry Andric   OverlapAggr.dumpHotFuncAndBlockOverlap(OS);
2669e8d8bef9SDimitry Andric   OverlapAggr.dumpFuncSimilarity(OS);
2670e8d8bef9SDimitry Andric }
2671e8d8bef9SDimitry Andric 
overlap_main()2672*0fca6ea1SDimitry Andric static int overlap_main() {
26730b57cec5SDimitry Andric   std::error_code EC;
26745f757f3fSDimitry Andric   raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);
26750b57cec5SDimitry Andric   if (EC)
26765f757f3fSDimitry Andric     exitWithErrorCode(EC, OutputFilename);
26770b57cec5SDimitry Andric 
2678e8d8bef9SDimitry Andric   if (ProfileKind == instr)
26790b57cec5SDimitry Andric     overlapInstrProfile(BaseFilename, TestFilename,
26805f757f3fSDimitry Andric                         OverlapFuncFilters{OverlapValueCutoff, FuncNameFilter},
26815f757f3fSDimitry Andric                         OS, IsCS);
2682e8d8bef9SDimitry Andric   else
2683e8d8bef9SDimitry Andric     overlapSampleProfile(BaseFilename, TestFilename,
26845f757f3fSDimitry Andric                          OverlapFuncFilters{OverlapValueCutoff, FuncNameFilter},
2685e8d8bef9SDimitry Andric                          SimilarityCutoff, OS);
26860b57cec5SDimitry Andric 
26870b57cec5SDimitry Andric   return 0;
26880b57cec5SDimitry Andric }
26890b57cec5SDimitry Andric 
2690fe6060f1SDimitry Andric namespace {
2691fe6060f1SDimitry Andric struct ValueSitesStats {
2692cb14a3feSDimitry Andric   ValueSitesStats() = default;
2693cb14a3feSDimitry Andric   uint64_t TotalNumValueSites = 0;
2694cb14a3feSDimitry Andric   uint64_t TotalNumValueSitesWithValueProfile = 0;
2695cb14a3feSDimitry Andric   uint64_t TotalNumValues = 0;
26960b57cec5SDimitry Andric   std::vector<unsigned> ValueSitesHistogram;
2697fe6060f1SDimitry Andric };
2698fe6060f1SDimitry Andric } // namespace
26990b57cec5SDimitry Andric 
traverseAllValueSites(const InstrProfRecord & Func,uint32_t VK,ValueSitesStats & Stats,raw_fd_ostream & OS,InstrProfSymtab * Symtab)27000b57cec5SDimitry Andric static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK,
27010b57cec5SDimitry Andric                                   ValueSitesStats &Stats, raw_fd_ostream &OS,
27020b57cec5SDimitry Andric                                   InstrProfSymtab *Symtab) {
27030b57cec5SDimitry Andric   uint32_t NS = Func.getNumValueSites(VK);
27040b57cec5SDimitry Andric   Stats.TotalNumValueSites += NS;
27050b57cec5SDimitry Andric   for (size_t I = 0; I < NS; ++I) {
2706*0fca6ea1SDimitry Andric     auto VD = Func.getValueArrayForSite(VK, I);
2707*0fca6ea1SDimitry Andric     uint32_t NV = VD.size();
2708*0fca6ea1SDimitry Andric     if (NV == 0)
2709*0fca6ea1SDimitry Andric       continue;
27100b57cec5SDimitry Andric     Stats.TotalNumValues += NV;
27110b57cec5SDimitry Andric     Stats.TotalNumValueSitesWithValueProfile++;
27120b57cec5SDimitry Andric     if (NV > Stats.ValueSitesHistogram.size())
27130b57cec5SDimitry Andric       Stats.ValueSitesHistogram.resize(NV, 0);
27140b57cec5SDimitry Andric     Stats.ValueSitesHistogram[NV - 1]++;
27150b57cec5SDimitry Andric 
27160b57cec5SDimitry Andric     uint64_t SiteSum = 0;
2717*0fca6ea1SDimitry Andric     for (const auto &V : VD)
2718*0fca6ea1SDimitry Andric       SiteSum += V.Count;
27190b57cec5SDimitry Andric     if (SiteSum == 0)
27200b57cec5SDimitry Andric       SiteSum = 1;
27210b57cec5SDimitry Andric 
2722*0fca6ea1SDimitry Andric     for (const auto &V : VD) {
27230b57cec5SDimitry Andric       OS << "\t[ " << format("%2u", I) << ", ";
27240b57cec5SDimitry Andric       if (Symtab == nullptr)
2725*0fca6ea1SDimitry Andric         OS << format("%4" PRIu64, V.Value);
27260b57cec5SDimitry Andric       else
2727*0fca6ea1SDimitry Andric         OS << Symtab->getFuncOrVarName(V.Value);
2728*0fca6ea1SDimitry Andric       OS << ", " << format("%10" PRId64, V.Count) << " ] ("
2729*0fca6ea1SDimitry Andric          << format("%.2f%%", (V.Count * 100.0 / SiteSum)) << ")\n";
27300b57cec5SDimitry Andric     }
27310b57cec5SDimitry Andric   }
27320b57cec5SDimitry Andric }
27330b57cec5SDimitry Andric 
showValueSitesStats(raw_fd_ostream & OS,uint32_t VK,ValueSitesStats & Stats)27340b57cec5SDimitry Andric static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK,
27350b57cec5SDimitry Andric                                 ValueSitesStats &Stats) {
27360b57cec5SDimitry Andric   OS << "  Total number of sites: " << Stats.TotalNumValueSites << "\n";
27370b57cec5SDimitry Andric   OS << "  Total number of sites with values: "
27380b57cec5SDimitry Andric      << Stats.TotalNumValueSitesWithValueProfile << "\n";
27390b57cec5SDimitry Andric   OS << "  Total number of profiled values: " << Stats.TotalNumValues << "\n";
27400b57cec5SDimitry Andric 
27410b57cec5SDimitry Andric   OS << "  Value sites histogram:\n\tNumTargets, SiteCount\n";
27420b57cec5SDimitry Andric   for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) {
27430b57cec5SDimitry Andric     if (Stats.ValueSitesHistogram[I] > 0)
27440b57cec5SDimitry Andric       OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n";
27450b57cec5SDimitry Andric   }
27460b57cec5SDimitry Andric }
27470b57cec5SDimitry Andric 
showInstrProfile(ShowFormat SFormat,raw_fd_ostream & OS)27485f757f3fSDimitry Andric static int showInstrProfile(ShowFormat SFormat, raw_fd_ostream &OS) {
2749bdd1243dSDimitry Andric   if (SFormat == ShowFormat::Json)
2750bdd1243dSDimitry Andric     exitWithError("JSON output is not supported for instr profiles");
2751bdd1243dSDimitry Andric   if (SFormat == ShowFormat::Yaml)
2752bdd1243dSDimitry Andric     exitWithError("YAML output is not supported for instr profiles");
275306c3fb27SDimitry Andric   auto FS = vfs::getRealFileSystem();
275406c3fb27SDimitry Andric   auto ReaderOrErr = InstrProfReader::create(Filename, *FS);
27550b57cec5SDimitry Andric   std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs);
27560b57cec5SDimitry Andric   if (ShowDetailedSummary && Cutoffs.empty()) {
275781ad6265SDimitry Andric     Cutoffs = ProfileSummaryBuilder::DefaultCutoffs;
27580b57cec5SDimitry Andric   }
27590b57cec5SDimitry Andric   InstrProfSummaryBuilder Builder(std::move(Cutoffs));
27600b57cec5SDimitry Andric   if (Error E = ReaderOrErr.takeError())
27610b57cec5SDimitry Andric     exitWithError(std::move(E), Filename);
27620b57cec5SDimitry Andric 
27630b57cec5SDimitry Andric   auto Reader = std::move(ReaderOrErr.get());
27640b57cec5SDimitry Andric   bool IsIRInstr = Reader->isIRLevelProfile();
27650b57cec5SDimitry Andric   size_t ShownFunctions = 0;
27660b57cec5SDimitry Andric   size_t BelowCutoffFunctions = 0;
27670b57cec5SDimitry Andric   int NumVPKind = IPVK_Last - IPVK_First + 1;
27680b57cec5SDimitry Andric   std::vector<ValueSitesStats> VPStats(NumVPKind);
27690b57cec5SDimitry Andric 
27700b57cec5SDimitry Andric   auto MinCmp = [](const std::pair<std::string, uint64_t> &v1,
27710b57cec5SDimitry Andric                    const std::pair<std::string, uint64_t> &v2) {
27720b57cec5SDimitry Andric     return v1.second > v2.second;
27730b57cec5SDimitry Andric   };
27740b57cec5SDimitry Andric 
27750b57cec5SDimitry Andric   std::priority_queue<std::pair<std::string, uint64_t>,
27760b57cec5SDimitry Andric                       std::vector<std::pair<std::string, uint64_t>>,
27770b57cec5SDimitry Andric                       decltype(MinCmp)>
27780b57cec5SDimitry Andric       HottestFuncs(MinCmp);
27790b57cec5SDimitry Andric 
27800b57cec5SDimitry Andric   if (!TextFormat && OnlyListBelow) {
27810b57cec5SDimitry Andric     OS << "The list of functions with the maximum counter less than "
27825f757f3fSDimitry Andric        << ShowValueCutoff << ":\n";
27830b57cec5SDimitry Andric   }
27840b57cec5SDimitry Andric 
27850b57cec5SDimitry Andric   // Add marker so that IR-level instrumentation round-trips properly.
27860b57cec5SDimitry Andric   if (TextFormat && IsIRInstr)
27870b57cec5SDimitry Andric     OS << ":ir\n";
27880b57cec5SDimitry Andric 
27890b57cec5SDimitry Andric   for (const auto &Func : *Reader) {
27900b57cec5SDimitry Andric     if (Reader->isIRLevelProfile()) {
27910b57cec5SDimitry Andric       bool FuncIsCS = NamedInstrProfRecord::hasCSFlagInHash(Func.Hash);
27920b57cec5SDimitry Andric       if (FuncIsCS != ShowCS)
27930b57cec5SDimitry Andric         continue;
27940b57cec5SDimitry Andric     }
2795349cc55cSDimitry Andric     bool Show = ShowAllFunctions ||
27965f757f3fSDimitry Andric                 (!FuncNameFilter.empty() && Func.Name.contains(FuncNameFilter));
27970b57cec5SDimitry Andric 
27980b57cec5SDimitry Andric     bool doTextFormatDump = (Show && TextFormat);
27990b57cec5SDimitry Andric 
28000b57cec5SDimitry Andric     if (doTextFormatDump) {
28010b57cec5SDimitry Andric       InstrProfSymtab &Symtab = Reader->getSymtab();
28020b57cec5SDimitry Andric       InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab,
28030b57cec5SDimitry Andric                                          OS);
28040b57cec5SDimitry Andric       continue;
28050b57cec5SDimitry Andric     }
28060b57cec5SDimitry Andric 
28070b57cec5SDimitry Andric     assert(Func.Counts.size() > 0 && "function missing entry counter");
28080b57cec5SDimitry Andric     Builder.addRecord(Func);
28090b57cec5SDimitry Andric 
28101fd87a68SDimitry Andric     if (ShowCovered) {
2811972a253aSDimitry Andric       if (llvm::any_of(Func.Counts, [](uint64_t C) { return C; }))
28121fd87a68SDimitry Andric         OS << Func.Name << "\n";
28131fd87a68SDimitry Andric       continue;
28141fd87a68SDimitry Andric     }
28151fd87a68SDimitry Andric 
28160b57cec5SDimitry Andric     uint64_t FuncMax = 0;
28170b57cec5SDimitry Andric     uint64_t FuncSum = 0;
2818bdd1243dSDimitry Andric 
2819bdd1243dSDimitry Andric     auto PseudoKind = Func.getCountPseudoKind();
2820bdd1243dSDimitry Andric     if (PseudoKind != InstrProfRecord::NotPseudo) {
2821bdd1243dSDimitry Andric       if (Show) {
2822bdd1243dSDimitry Andric         if (!ShownFunctions)
2823bdd1243dSDimitry Andric           OS << "Counters:\n";
2824bdd1243dSDimitry Andric         ++ShownFunctions;
2825bdd1243dSDimitry Andric         OS << "  " << Func.Name << ":\n"
2826bdd1243dSDimitry Andric            << "    Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n"
2827bdd1243dSDimitry Andric            << "    Counters: " << Func.Counts.size();
2828bdd1243dSDimitry Andric         if (PseudoKind == InstrProfRecord::PseudoHot)
2829bdd1243dSDimitry Andric           OS << "    <PseudoHot>\n";
2830bdd1243dSDimitry Andric         else if (PseudoKind == InstrProfRecord::PseudoWarm)
2831bdd1243dSDimitry Andric           OS << "    <PseudoWarm>\n";
2832bdd1243dSDimitry Andric         else
2833bdd1243dSDimitry Andric           llvm_unreachable("Unknown PseudoKind");
2834bdd1243dSDimitry Andric       }
2835e8d8bef9SDimitry Andric       continue;
2836bdd1243dSDimitry Andric     }
2837bdd1243dSDimitry Andric 
2838bdd1243dSDimitry Andric     for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) {
28390b57cec5SDimitry Andric       FuncMax = std::max(FuncMax, Func.Counts[I]);
28400b57cec5SDimitry Andric       FuncSum += Func.Counts[I];
28410b57cec5SDimitry Andric     }
28420b57cec5SDimitry Andric 
28435f757f3fSDimitry Andric     if (FuncMax < ShowValueCutoff) {
28440b57cec5SDimitry Andric       ++BelowCutoffFunctions;
28450b57cec5SDimitry Andric       if (OnlyListBelow) {
28460b57cec5SDimitry Andric         OS << "  " << Func.Name << ": (Max = " << FuncMax
28470b57cec5SDimitry Andric            << " Sum = " << FuncSum << ")\n";
28480b57cec5SDimitry Andric       }
28490b57cec5SDimitry Andric       continue;
28500b57cec5SDimitry Andric     } else if (OnlyListBelow)
28510b57cec5SDimitry Andric       continue;
28520b57cec5SDimitry Andric 
28535f757f3fSDimitry Andric     if (TopNFunctions) {
28545f757f3fSDimitry Andric       if (HottestFuncs.size() == TopNFunctions) {
28550b57cec5SDimitry Andric         if (HottestFuncs.top().second < FuncMax) {
28560b57cec5SDimitry Andric           HottestFuncs.pop();
28570b57cec5SDimitry Andric           HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
28580b57cec5SDimitry Andric         }
28590b57cec5SDimitry Andric       } else
28600b57cec5SDimitry Andric         HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
28610b57cec5SDimitry Andric     }
28620b57cec5SDimitry Andric 
28630b57cec5SDimitry Andric     if (Show) {
28640b57cec5SDimitry Andric       if (!ShownFunctions)
28650b57cec5SDimitry Andric         OS << "Counters:\n";
28660b57cec5SDimitry Andric 
28670b57cec5SDimitry Andric       ++ShownFunctions;
28680b57cec5SDimitry Andric 
28690b57cec5SDimitry Andric       OS << "  " << Func.Name << ":\n"
28700b57cec5SDimitry Andric          << "    Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n"
28710b57cec5SDimitry Andric          << "    Counters: " << Func.Counts.size() << "\n";
28720b57cec5SDimitry Andric       if (!IsIRInstr)
28730b57cec5SDimitry Andric         OS << "    Function count: " << Func.Counts[0] << "\n";
28740b57cec5SDimitry Andric 
28750b57cec5SDimitry Andric       if (ShowIndirectCallTargets)
28760b57cec5SDimitry Andric         OS << "    Indirect Call Site Count: "
28770b57cec5SDimitry Andric            << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n";
28780b57cec5SDimitry Andric 
2879*0fca6ea1SDimitry Andric       if (ShowVTables)
2880*0fca6ea1SDimitry Andric         OS << "    Number of instrumented vtables: "
2881*0fca6ea1SDimitry Andric            << Func.getNumValueSites(IPVK_VTableTarget) << "\n";
2882*0fca6ea1SDimitry Andric 
28830b57cec5SDimitry Andric       uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize);
28840b57cec5SDimitry Andric       if (ShowMemOPSizes && NumMemOPCalls > 0)
28850b57cec5SDimitry Andric         OS << "    Number of Memory Intrinsics Calls: " << NumMemOPCalls
28860b57cec5SDimitry Andric            << "\n";
28870b57cec5SDimitry Andric 
28880b57cec5SDimitry Andric       if (ShowCounts) {
28890b57cec5SDimitry Andric         OS << "    Block counts: [";
28900b57cec5SDimitry Andric         size_t Start = (IsIRInstr ? 0 : 1);
28910b57cec5SDimitry Andric         for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) {
28920b57cec5SDimitry Andric           OS << (I == Start ? "" : ", ") << Func.Counts[I];
28930b57cec5SDimitry Andric         }
28940b57cec5SDimitry Andric         OS << "]\n";
28950b57cec5SDimitry Andric       }
28960b57cec5SDimitry Andric 
28970b57cec5SDimitry Andric       if (ShowIndirectCallTargets) {
28980b57cec5SDimitry Andric         OS << "    Indirect Target Results:\n";
28990b57cec5SDimitry Andric         traverseAllValueSites(Func, IPVK_IndirectCallTarget,
29000b57cec5SDimitry Andric                               VPStats[IPVK_IndirectCallTarget], OS,
29010b57cec5SDimitry Andric                               &(Reader->getSymtab()));
29020b57cec5SDimitry Andric       }
29030b57cec5SDimitry Andric 
2904*0fca6ea1SDimitry Andric       if (ShowVTables) {
2905*0fca6ea1SDimitry Andric         OS << "    VTable Results:\n";
2906*0fca6ea1SDimitry Andric         traverseAllValueSites(Func, IPVK_VTableTarget,
2907*0fca6ea1SDimitry Andric                               VPStats[IPVK_VTableTarget], OS,
2908*0fca6ea1SDimitry Andric                               &(Reader->getSymtab()));
2909*0fca6ea1SDimitry Andric       }
2910*0fca6ea1SDimitry Andric 
29110b57cec5SDimitry Andric       if (ShowMemOPSizes && NumMemOPCalls > 0) {
29120b57cec5SDimitry Andric         OS << "    Memory Intrinsic Size Results:\n";
29130b57cec5SDimitry Andric         traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS,
29140b57cec5SDimitry Andric                               nullptr);
29150b57cec5SDimitry Andric       }
29160b57cec5SDimitry Andric     }
29170b57cec5SDimitry Andric   }
29180b57cec5SDimitry Andric   if (Reader->hasError())
29190b57cec5SDimitry Andric     exitWithError(Reader->getError(), Filename);
29200b57cec5SDimitry Andric 
29211fd87a68SDimitry Andric   if (TextFormat || ShowCovered)
29220b57cec5SDimitry Andric     return 0;
29230b57cec5SDimitry Andric   std::unique_ptr<ProfileSummary> PS(Builder.getSummary());
2924e8d8bef9SDimitry Andric   bool IsIR = Reader->isIRLevelProfile();
2925e8d8bef9SDimitry Andric   OS << "Instrumentation level: " << (IsIR ? "IR" : "Front-end");
2926e8d8bef9SDimitry Andric   if (IsIR)
2927e8d8bef9SDimitry Andric     OS << "  entry_first = " << Reader->instrEntryBBEnabled();
2928e8d8bef9SDimitry Andric   OS << "\n";
29295f757f3fSDimitry Andric   if (ShowAllFunctions || !FuncNameFilter.empty())
29300b57cec5SDimitry Andric     OS << "Functions shown: " << ShownFunctions << "\n";
29310b57cec5SDimitry Andric   OS << "Total functions: " << PS->getNumFunctions() << "\n";
29325f757f3fSDimitry Andric   if (ShowValueCutoff > 0) {
29335f757f3fSDimitry Andric     OS << "Number of functions with maximum count (< " << ShowValueCutoff
29340b57cec5SDimitry Andric        << "): " << BelowCutoffFunctions << "\n";
29355f757f3fSDimitry Andric     OS << "Number of functions with maximum count (>= " << ShowValueCutoff
29360b57cec5SDimitry Andric        << "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n";
29370b57cec5SDimitry Andric   }
29380b57cec5SDimitry Andric   OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n";
29390b57cec5SDimitry Andric   OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n";
29400b57cec5SDimitry Andric 
29415f757f3fSDimitry Andric   if (TopNFunctions) {
29420b57cec5SDimitry Andric     std::vector<std::pair<std::string, uint64_t>> SortedHottestFuncs;
29430b57cec5SDimitry Andric     while (!HottestFuncs.empty()) {
29440b57cec5SDimitry Andric       SortedHottestFuncs.emplace_back(HottestFuncs.top());
29450b57cec5SDimitry Andric       HottestFuncs.pop();
29460b57cec5SDimitry Andric     }
29475f757f3fSDimitry Andric     OS << "Top " << TopNFunctions
29480b57cec5SDimitry Andric        << " functions with the largest internal block counts: \n";
29490b57cec5SDimitry Andric     for (auto &hotfunc : llvm::reverse(SortedHottestFuncs))
29500b57cec5SDimitry Andric       OS << "  " << hotfunc.first << ", max count = " << hotfunc.second << "\n";
29510b57cec5SDimitry Andric   }
29520b57cec5SDimitry Andric 
29530b57cec5SDimitry Andric   if (ShownFunctions && ShowIndirectCallTargets) {
29540b57cec5SDimitry Andric     OS << "Statistics for indirect call sites profile:\n";
29550b57cec5SDimitry Andric     showValueSitesStats(OS, IPVK_IndirectCallTarget,
29560b57cec5SDimitry Andric                         VPStats[IPVK_IndirectCallTarget]);
29570b57cec5SDimitry Andric   }
29580b57cec5SDimitry Andric 
2959*0fca6ea1SDimitry Andric   if (ShownFunctions && ShowVTables) {
2960*0fca6ea1SDimitry Andric     OS << "Statistics for vtable profile:\n";
2961*0fca6ea1SDimitry Andric     showValueSitesStats(OS, IPVK_VTableTarget, VPStats[IPVK_VTableTarget]);
2962*0fca6ea1SDimitry Andric   }
2963*0fca6ea1SDimitry Andric 
29640b57cec5SDimitry Andric   if (ShownFunctions && ShowMemOPSizes) {
29650b57cec5SDimitry Andric     OS << "Statistics for memory intrinsic calls sizes profile:\n";
29660b57cec5SDimitry Andric     showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]);
29670b57cec5SDimitry Andric   }
29680b57cec5SDimitry Andric 
29690b57cec5SDimitry Andric   if (ShowDetailedSummary) {
29700b57cec5SDimitry Andric     OS << "Total number of blocks: " << PS->getNumCounts() << "\n";
29710b57cec5SDimitry Andric     OS << "Total count: " << PS->getTotalCount() << "\n";
29725ffd83dbSDimitry Andric     PS->printDetailedSummary(OS);
29730b57cec5SDimitry Andric   }
2974fe6060f1SDimitry Andric 
2975fe6060f1SDimitry Andric   if (ShowBinaryIds)
2976fe6060f1SDimitry Andric     if (Error E = Reader->printBinaryIds(OS))
2977fe6060f1SDimitry Andric       exitWithError(std::move(E), Filename);
2978fe6060f1SDimitry Andric 
2979bdd1243dSDimitry Andric   if (ShowProfileVersion)
2980bdd1243dSDimitry Andric     OS << "Profile version: " << Reader->getVersion() << "\n";
298106c3fb27SDimitry Andric 
298206c3fb27SDimitry Andric   if (ShowTemporalProfTraces) {
298306c3fb27SDimitry Andric     auto &Traces = Reader->getTemporalProfTraces();
298406c3fb27SDimitry Andric     OS << "Temporal Profile Traces (samples=" << Traces.size()
298506c3fb27SDimitry Andric        << " seen=" << Reader->getTemporalProfTraceStreamSize() << "):\n";
298606c3fb27SDimitry Andric     for (unsigned i = 0; i < Traces.size(); i++) {
298706c3fb27SDimitry Andric       OS << "  Temporal Profile Trace " << i << " (weight=" << Traces[i].Weight
298806c3fb27SDimitry Andric          << " count=" << Traces[i].FunctionNameRefs.size() << "):\n";
298906c3fb27SDimitry Andric       for (auto &NameRef : Traces[i].FunctionNameRefs)
29905f757f3fSDimitry Andric         OS << "    " << Reader->getSymtab().getFuncOrVarName(NameRef) << "\n";
299106c3fb27SDimitry Andric     }
299206c3fb27SDimitry Andric   }
299306c3fb27SDimitry Andric 
29940b57cec5SDimitry Andric   return 0;
29950b57cec5SDimitry Andric }
29960b57cec5SDimitry Andric 
showSectionInfo(sampleprof::SampleProfileReader * Reader,raw_fd_ostream & OS)29978bcb0991SDimitry Andric static void showSectionInfo(sampleprof::SampleProfileReader *Reader,
29988bcb0991SDimitry Andric                             raw_fd_ostream &OS) {
29998bcb0991SDimitry Andric   if (!Reader->dumpSectionInfo(OS)) {
30008bcb0991SDimitry Andric     WithColor::warning() << "-show-sec-info-only is only supported for "
30018bcb0991SDimitry Andric                          << "sample profile in extbinary format and is "
30028bcb0991SDimitry Andric                          << "ignored for other formats.\n";
30038bcb0991SDimitry Andric     return;
30048bcb0991SDimitry Andric   }
30058bcb0991SDimitry Andric }
30068bcb0991SDimitry Andric 
30075ffd83dbSDimitry Andric namespace {
30085ffd83dbSDimitry Andric struct HotFuncInfo {
3009349cc55cSDimitry Andric   std::string FuncName;
3010cb14a3feSDimitry Andric   uint64_t TotalCount = 0;
3011cb14a3feSDimitry Andric   double TotalCountPercent = 0.0f;
3012cb14a3feSDimitry Andric   uint64_t MaxCount = 0;
3013cb14a3feSDimitry Andric   uint64_t EntryCount = 0;
30145ffd83dbSDimitry Andric 
3015cb14a3feSDimitry Andric   HotFuncInfo() = default;
30165ffd83dbSDimitry Andric 
HotFuncInfo__anon209d874b1611::HotFuncInfo30175ffd83dbSDimitry Andric   HotFuncInfo(StringRef FN, uint64_t TS, double TSP, uint64_t MS, uint64_t ES)
3018349cc55cSDimitry Andric       : FuncName(FN.begin(), FN.end()), TotalCount(TS), TotalCountPercent(TSP),
3019349cc55cSDimitry Andric         MaxCount(MS), EntryCount(ES) {}
30205ffd83dbSDimitry Andric };
30215ffd83dbSDimitry Andric } // namespace
30225ffd83dbSDimitry Andric 
30235ffd83dbSDimitry Andric // Print out detailed information about hot functions in PrintValues vector.
30245ffd83dbSDimitry Andric // Users specify titles and offset of every columns through ColumnTitle and
30255ffd83dbSDimitry Andric // ColumnOffset. The size of ColumnTitle and ColumnOffset need to be the same
30265ffd83dbSDimitry Andric // and at least 4. Besides, users can optionally give a HotFuncMetric string to
30275ffd83dbSDimitry 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)30285ffd83dbSDimitry Andric static void dumpHotFunctionList(const std::vector<std::string> &ColumnTitle,
30295ffd83dbSDimitry Andric                                 const std::vector<int> &ColumnOffset,
30305ffd83dbSDimitry Andric                                 const std::vector<HotFuncInfo> &PrintValues,
30315ffd83dbSDimitry Andric                                 uint64_t HotFuncCount, uint64_t TotalFuncCount,
30325ffd83dbSDimitry Andric                                 uint64_t HotProfCount, uint64_t TotalProfCount,
30335ffd83dbSDimitry Andric                                 const std::string &HotFuncMetric,
3034349cc55cSDimitry Andric                                 uint32_t TopNFunctions, raw_fd_ostream &OS) {
3035e8d8bef9SDimitry Andric   assert(ColumnOffset.size() == ColumnTitle.size() &&
3036e8d8bef9SDimitry Andric          "ColumnOffset and ColumnTitle should have the same size");
3037e8d8bef9SDimitry Andric   assert(ColumnTitle.size() >= 4 &&
3038e8d8bef9SDimitry Andric          "ColumnTitle should have at least 4 elements");
3039e8d8bef9SDimitry Andric   assert(TotalFuncCount > 0 &&
3040e8d8bef9SDimitry Andric          "There should be at least one function in the profile");
30415ffd83dbSDimitry Andric   double TotalProfPercent = 0;
30425ffd83dbSDimitry Andric   if (TotalProfCount > 0)
3043e8d8bef9SDimitry Andric     TotalProfPercent = static_cast<double>(HotProfCount) / TotalProfCount * 100;
30445ffd83dbSDimitry Andric 
30455ffd83dbSDimitry Andric   formatted_raw_ostream FOS(OS);
30465ffd83dbSDimitry Andric   FOS << HotFuncCount << " out of " << TotalFuncCount
30475ffd83dbSDimitry Andric       << " functions with profile ("
3048e8d8bef9SDimitry Andric       << format("%.2f%%",
3049e8d8bef9SDimitry Andric                 (static_cast<double>(HotFuncCount) / TotalFuncCount * 100))
30505ffd83dbSDimitry Andric       << ") are considered hot functions";
30515ffd83dbSDimitry Andric   if (!HotFuncMetric.empty())
30525ffd83dbSDimitry Andric     FOS << " (" << HotFuncMetric << ")";
30535ffd83dbSDimitry Andric   FOS << ".\n";
30545ffd83dbSDimitry Andric   FOS << HotProfCount << " out of " << TotalProfCount << " profile counts ("
30555ffd83dbSDimitry Andric       << format("%.2f%%", TotalProfPercent) << ") are from hot functions.\n";
30565ffd83dbSDimitry Andric 
30575ffd83dbSDimitry Andric   for (size_t I = 0; I < ColumnTitle.size(); ++I) {
30585ffd83dbSDimitry Andric     FOS.PadToColumn(ColumnOffset[I]);
30595ffd83dbSDimitry Andric     FOS << ColumnTitle[I];
30605ffd83dbSDimitry Andric   }
30615ffd83dbSDimitry Andric   FOS << "\n";
30625ffd83dbSDimitry Andric 
3063349cc55cSDimitry Andric   uint32_t Count = 0;
3064349cc55cSDimitry Andric   for (const auto &R : PrintValues) {
3065349cc55cSDimitry Andric     if (TopNFunctions && (Count++ == TopNFunctions))
3066349cc55cSDimitry Andric       break;
30675ffd83dbSDimitry Andric     FOS.PadToColumn(ColumnOffset[0]);
30685ffd83dbSDimitry Andric     FOS << R.TotalCount << " (" << format("%.2f%%", R.TotalCountPercent) << ")";
30695ffd83dbSDimitry Andric     FOS.PadToColumn(ColumnOffset[1]);
30705ffd83dbSDimitry Andric     FOS << R.MaxCount;
30715ffd83dbSDimitry Andric     FOS.PadToColumn(ColumnOffset[2]);
30725ffd83dbSDimitry Andric     FOS << R.EntryCount;
30735ffd83dbSDimitry Andric     FOS.PadToColumn(ColumnOffset[3]);
30745ffd83dbSDimitry Andric     FOS << R.FuncName << "\n";
30755ffd83dbSDimitry Andric   }
30765ffd83dbSDimitry Andric }
30775ffd83dbSDimitry Andric 
showHotFunctionList(const sampleprof::SampleProfileMap & Profiles,ProfileSummary & PS,uint32_t TopN,raw_fd_ostream & OS)3078349cc55cSDimitry Andric static int showHotFunctionList(const sampleprof::SampleProfileMap &Profiles,
3079349cc55cSDimitry Andric                                ProfileSummary &PS, uint32_t TopN,
3080349cc55cSDimitry Andric                                raw_fd_ostream &OS) {
30815ffd83dbSDimitry Andric   using namespace sampleprof;
30825ffd83dbSDimitry Andric 
30835ffd83dbSDimitry Andric   const uint32_t HotFuncCutoff = 990000;
30845ffd83dbSDimitry Andric   auto &SummaryVector = PS.getDetailedSummary();
30855ffd83dbSDimitry Andric   uint64_t MinCountThreshold = 0;
30865ffd83dbSDimitry Andric   for (const ProfileSummaryEntry &SummaryEntry : SummaryVector) {
30875ffd83dbSDimitry Andric     if (SummaryEntry.Cutoff == HotFuncCutoff) {
30885ffd83dbSDimitry Andric       MinCountThreshold = SummaryEntry.MinCount;
30895ffd83dbSDimitry Andric       break;
30905ffd83dbSDimitry Andric     }
30915ffd83dbSDimitry Andric   }
30925ffd83dbSDimitry Andric 
30935ffd83dbSDimitry Andric   // Traverse all functions in the profile and keep only hot functions.
30945ffd83dbSDimitry Andric   // The following loop also calculates the sum of total samples of all
30955ffd83dbSDimitry Andric   // functions.
30965ffd83dbSDimitry Andric   std::multimap<uint64_t, std::pair<const FunctionSamples *, const uint64_t>,
30975ffd83dbSDimitry Andric                 std::greater<uint64_t>>
30985ffd83dbSDimitry Andric       HotFunc;
30995ffd83dbSDimitry Andric   uint64_t ProfileTotalSample = 0;
31005ffd83dbSDimitry Andric   uint64_t HotFuncSample = 0;
31015ffd83dbSDimitry Andric   uint64_t HotFuncCount = 0;
3102e8d8bef9SDimitry Andric 
31035ffd83dbSDimitry Andric   for (const auto &I : Profiles) {
3104e8d8bef9SDimitry Andric     FuncSampleStats FuncStats;
31055ffd83dbSDimitry Andric     const FunctionSamples &FuncProf = I.second;
31065ffd83dbSDimitry Andric     ProfileTotalSample += FuncProf.getTotalSamples();
3107e8d8bef9SDimitry Andric     getFuncSampleStats(FuncProf, FuncStats, MinCountThreshold);
31085ffd83dbSDimitry Andric 
3109e8d8bef9SDimitry Andric     if (isFunctionHot(FuncStats, MinCountThreshold)) {
31105ffd83dbSDimitry Andric       HotFunc.emplace(FuncProf.getTotalSamples(),
3111e8d8bef9SDimitry Andric                       std::make_pair(&(I.second), FuncStats.MaxSample));
31125ffd83dbSDimitry Andric       HotFuncSample += FuncProf.getTotalSamples();
31135ffd83dbSDimitry Andric       ++HotFuncCount;
31145ffd83dbSDimitry Andric     }
31155ffd83dbSDimitry Andric   }
31165ffd83dbSDimitry Andric 
31175ffd83dbSDimitry Andric   std::vector<std::string> ColumnTitle{"Total sample (%)", "Max sample",
31185ffd83dbSDimitry Andric                                        "Entry sample", "Function name"};
31195ffd83dbSDimitry Andric   std::vector<int> ColumnOffset{0, 24, 42, 58};
31205ffd83dbSDimitry Andric   std::string Metric =
31215ffd83dbSDimitry Andric       std::string("max sample >= ") + std::to_string(MinCountThreshold);
31225ffd83dbSDimitry Andric   std::vector<HotFuncInfo> PrintValues;
31235ffd83dbSDimitry Andric   for (const auto &FuncPair : HotFunc) {
31245ffd83dbSDimitry Andric     const FunctionSamples &Func = *FuncPair.second.first;
31255ffd83dbSDimitry Andric     double TotalSamplePercent =
31265ffd83dbSDimitry Andric         (ProfileTotalSample > 0)
31275ffd83dbSDimitry Andric             ? (Func.getTotalSamples() * 100.0) / ProfileTotalSample
31285ffd83dbSDimitry Andric             : 0;
3129fcaf7f86SDimitry Andric     PrintValues.emplace_back(
3130fcaf7f86SDimitry Andric         HotFuncInfo(Func.getContext().toString(), Func.getTotalSamples(),
3131fcaf7f86SDimitry Andric                     TotalSamplePercent, FuncPair.second.second,
3132fcaf7f86SDimitry Andric                     Func.getHeadSamplesEstimate()));
31335ffd83dbSDimitry Andric   }
31345ffd83dbSDimitry Andric   dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount,
31355ffd83dbSDimitry Andric                       Profiles.size(), HotFuncSample, ProfileTotalSample,
3136349cc55cSDimitry Andric                       Metric, TopN, OS);
31375ffd83dbSDimitry Andric 
31385ffd83dbSDimitry Andric   return 0;
31395ffd83dbSDimitry Andric }
31405ffd83dbSDimitry Andric 
showSampleProfile(ShowFormat SFormat,raw_fd_ostream & OS)31415f757f3fSDimitry Andric static int showSampleProfile(ShowFormat SFormat, raw_fd_ostream &OS) {
3142bdd1243dSDimitry Andric   if (SFormat == ShowFormat::Yaml)
3143bdd1243dSDimitry Andric     exitWithError("YAML output is not supported for sample profiles");
31440b57cec5SDimitry Andric   using namespace sampleprof;
31450b57cec5SDimitry Andric   LLVMContext Context;
314606c3fb27SDimitry Andric   auto FS = vfs::getRealFileSystem();
314706c3fb27SDimitry Andric   auto ReaderOrErr = SampleProfileReader::create(Filename, Context, *FS,
314806c3fb27SDimitry Andric                                                  FSDiscriminatorPassOption);
31490b57cec5SDimitry Andric   if (std::error_code EC = ReaderOrErr.getError())
31500b57cec5SDimitry Andric     exitWithErrorCode(EC, Filename);
31510b57cec5SDimitry Andric 
31520b57cec5SDimitry Andric   auto Reader = std::move(ReaderOrErr.get());
31538bcb0991SDimitry Andric   if (ShowSectionInfoOnly) {
31548bcb0991SDimitry Andric     showSectionInfo(Reader.get(), OS);
31558bcb0991SDimitry Andric     return 0;
31568bcb0991SDimitry Andric   }
31578bcb0991SDimitry Andric 
31580b57cec5SDimitry Andric   if (std::error_code EC = Reader->read())
31590b57cec5SDimitry Andric     exitWithErrorCode(EC, Filename);
31600b57cec5SDimitry Andric 
31615f757f3fSDimitry Andric   if (ShowAllFunctions || FuncNameFilter.empty()) {
3162bdd1243dSDimitry Andric     if (SFormat == ShowFormat::Json)
3163bdd1243dSDimitry Andric       Reader->dumpJson(OS);
31640b57cec5SDimitry Andric     else
3165bdd1243dSDimitry Andric       Reader->dump(OS);
3166bdd1243dSDimitry Andric   } else {
3167bdd1243dSDimitry Andric     if (SFormat == ShowFormat::Json)
3168bdd1243dSDimitry Andric       exitWithError(
3169bdd1243dSDimitry Andric           "the JSON format is supported only when all functions are to "
3170bdd1243dSDimitry Andric           "be printed");
3171bdd1243dSDimitry Andric 
3172349cc55cSDimitry Andric     // TODO: parse context string to support filtering by contexts.
31735f757f3fSDimitry Andric     FunctionSamples *FS = Reader->getSamplesFor(StringRef(FuncNameFilter));
31745f757f3fSDimitry Andric     Reader->dumpFunctionProfile(FS ? *FS : FunctionSamples(), OS);
3175bdd1243dSDimitry Andric   }
31760b57cec5SDimitry Andric 
31778bcb0991SDimitry Andric   if (ShowProfileSymbolList) {
31788bcb0991SDimitry Andric     std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =
31798bcb0991SDimitry Andric         Reader->getProfileSymbolList();
31808bcb0991SDimitry Andric     ReaderList->dump(OS);
31818bcb0991SDimitry Andric   }
31828bcb0991SDimitry Andric 
31835ffd83dbSDimitry Andric   if (ShowDetailedSummary) {
31845ffd83dbSDimitry Andric     auto &PS = Reader->getSummary();
31855ffd83dbSDimitry Andric     PS.printSummary(OS);
31865ffd83dbSDimitry Andric     PS.printDetailedSummary(OS);
31875ffd83dbSDimitry Andric   }
31885ffd83dbSDimitry Andric 
31895f757f3fSDimitry Andric   if (ShowHotFuncList || TopNFunctions)
31905f757f3fSDimitry Andric     showHotFunctionList(Reader->getProfiles(), Reader->getSummary(),
31915f757f3fSDimitry Andric                         TopNFunctions, OS);
31925ffd83dbSDimitry Andric 
31930b57cec5SDimitry Andric   return 0;
31940b57cec5SDimitry Andric }
31950b57cec5SDimitry Andric 
showMemProfProfile(ShowFormat SFormat,raw_fd_ostream & OS)31965f757f3fSDimitry Andric static int showMemProfProfile(ShowFormat SFormat, raw_fd_ostream &OS) {
3197bdd1243dSDimitry Andric   if (SFormat == ShowFormat::Json)
3198bdd1243dSDimitry Andric     exitWithError("JSON output is not supported for MemProf");
319981ad6265SDimitry Andric   auto ReaderOr = llvm::memprof::RawMemProfReader::create(
320081ad6265SDimitry Andric       Filename, ProfiledBinary, /*KeepNames=*/true);
32014824e7fdSDimitry Andric   if (Error E = ReaderOr.takeError())
320281ad6265SDimitry Andric     // Since the error can be related to the profile or the binary we do not
320381ad6265SDimitry Andric     // pass whence. Instead additional context is provided where necessary in
320481ad6265SDimitry Andric     // the error message.
320581ad6265SDimitry Andric     exitWithError(std::move(E), /*Whence*/ "");
32064824e7fdSDimitry Andric 
32074824e7fdSDimitry Andric   std::unique_ptr<llvm::memprof::RawMemProfReader> Reader(
32084824e7fdSDimitry Andric       ReaderOr.get().release());
320981ad6265SDimitry Andric 
321081ad6265SDimitry Andric   Reader->printYAML(OS);
32114824e7fdSDimitry Andric   return 0;
32124824e7fdSDimitry Andric }
32134824e7fdSDimitry Andric 
showDebugInfoCorrelation(const std::string & Filename,ShowFormat SFormat,raw_fd_ostream & OS)321404eeddc0SDimitry Andric static int showDebugInfoCorrelation(const std::string &Filename,
3215bdd1243dSDimitry Andric                                     ShowFormat SFormat, raw_fd_ostream &OS) {
3216bdd1243dSDimitry Andric   if (SFormat == ShowFormat::Json)
3217bdd1243dSDimitry Andric     exitWithError("JSON output is not supported for debug info correlation");
321804eeddc0SDimitry Andric   std::unique_ptr<InstrProfCorrelator> Correlator;
32195f757f3fSDimitry Andric   if (auto Err =
32205f757f3fSDimitry Andric           InstrProfCorrelator::get(Filename, InstrProfCorrelator::DEBUG_INFO)
32215f757f3fSDimitry Andric               .moveInto(Correlator))
322204eeddc0SDimitry Andric     exitWithError(std::move(Err), Filename);
3223bdd1243dSDimitry Andric   if (SFormat == ShowFormat::Yaml) {
32245f757f3fSDimitry Andric     if (auto Err = Correlator->dumpYaml(MaxDbgCorrelationWarnings, OS))
3225bdd1243dSDimitry Andric       exitWithError(std::move(Err), Filename);
3226bdd1243dSDimitry Andric     return 0;
3227bdd1243dSDimitry Andric   }
3228bdd1243dSDimitry Andric 
32295f757f3fSDimitry Andric   if (auto Err = Correlator->correlateProfileData(MaxDbgCorrelationWarnings))
323004eeddc0SDimitry Andric     exitWithError(std::move(Err), Filename);
323104eeddc0SDimitry Andric 
323204eeddc0SDimitry Andric   InstrProfSymtab Symtab;
323304eeddc0SDimitry Andric   if (auto Err = Symtab.create(
323404eeddc0SDimitry Andric           StringRef(Correlator->getNamesPointer(), Correlator->getNamesSize())))
323504eeddc0SDimitry Andric     exitWithError(std::move(Err), Filename);
323604eeddc0SDimitry Andric 
323704eeddc0SDimitry Andric   if (ShowProfileSymbolList)
323804eeddc0SDimitry Andric     Symtab.dumpNames(OS);
323904eeddc0SDimitry Andric   // TODO: Read "Profile Data Type" from debug info to compute and show how many
324004eeddc0SDimitry Andric   // counters the section holds.
324104eeddc0SDimitry Andric   if (ShowDetailedSummary)
324204eeddc0SDimitry Andric     OS << "Counters section size: 0x"
324304eeddc0SDimitry Andric        << Twine::utohexstr(Correlator->getCountersSectionSize()) << " bytes\n";
324404eeddc0SDimitry Andric   OS << "Found " << Correlator->getDataSize() << " functions\n";
324504eeddc0SDimitry Andric 
324604eeddc0SDimitry Andric   return 0;
324704eeddc0SDimitry Andric }
324804eeddc0SDimitry Andric 
show_main(StringRef ProgName)3249*0fca6ea1SDimitry Andric static int show_main(StringRef ProgName) {
325004eeddc0SDimitry Andric   if (Filename.empty() && DebugInfoFilename.empty())
325104eeddc0SDimitry Andric     exitWithError(
325204eeddc0SDimitry Andric         "the positional argument '<profdata-file>' is required unless '--" +
325304eeddc0SDimitry Andric         DebugInfoFilename.ArgStr + "' is provided");
325404eeddc0SDimitry Andric 
3255e8d8bef9SDimitry Andric   if (Filename == OutputFilename) {
3256*0fca6ea1SDimitry Andric     errs() << ProgName
3257*0fca6ea1SDimitry Andric            << " show: Input file name cannot be the same as the output file "
3258*0fca6ea1SDimitry Andric               "name!\n";
32590b57cec5SDimitry Andric     return 1;
32600b57cec5SDimitry Andric   }
3261bdd1243dSDimitry Andric   if (JsonFormat)
3262bdd1243dSDimitry Andric     SFormat = ShowFormat::Json;
32630b57cec5SDimitry Andric 
32640b57cec5SDimitry Andric   std::error_code EC;
3265fe6060f1SDimitry Andric   raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);
32660b57cec5SDimitry Andric   if (EC)
32670b57cec5SDimitry Andric     exitWithErrorCode(EC, OutputFilename);
32680b57cec5SDimitry Andric 
32695f757f3fSDimitry Andric   if (ShowAllFunctions && !FuncNameFilter.empty())
32700b57cec5SDimitry Andric     WithColor::warning() << "-function argument ignored: showing all functions\n";
32710b57cec5SDimitry Andric 
327204eeddc0SDimitry Andric   if (!DebugInfoFilename.empty())
32735f757f3fSDimitry Andric     return showDebugInfoCorrelation(DebugInfoFilename, SFormat, OS);
327404eeddc0SDimitry Andric 
32755f757f3fSDimitry Andric   if (ShowProfileKind == instr)
32765f757f3fSDimitry Andric     return showInstrProfile(SFormat, OS);
32775f757f3fSDimitry Andric   if (ShowProfileKind == sample)
32785f757f3fSDimitry Andric     return showSampleProfile(SFormat, OS);
32795f757f3fSDimitry Andric   return showMemProfProfile(SFormat, OS);
32800b57cec5SDimitry Andric }
32810b57cec5SDimitry Andric 
order_main()3282*0fca6ea1SDimitry Andric static int order_main() {
328306c3fb27SDimitry Andric   std::error_code EC;
328406c3fb27SDimitry Andric   raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);
328506c3fb27SDimitry Andric   if (EC)
328606c3fb27SDimitry Andric     exitWithErrorCode(EC, OutputFilename);
328706c3fb27SDimitry Andric   auto FS = vfs::getRealFileSystem();
328806c3fb27SDimitry Andric   auto ReaderOrErr = InstrProfReader::create(Filename, *FS);
328906c3fb27SDimitry Andric   if (Error E = ReaderOrErr.takeError())
329006c3fb27SDimitry Andric     exitWithError(std::move(E), Filename);
329106c3fb27SDimitry Andric 
329206c3fb27SDimitry Andric   auto Reader = std::move(ReaderOrErr.get());
329306c3fb27SDimitry Andric   for (auto &I : *Reader) {
329406c3fb27SDimitry Andric     // Read all entries
329506c3fb27SDimitry Andric     (void)I;
329606c3fb27SDimitry Andric   }
3297*0fca6ea1SDimitry Andric   ArrayRef Traces = Reader->getTemporalProfTraces();
3298*0fca6ea1SDimitry Andric   if (NumTestTraces && NumTestTraces >= Traces.size())
3299*0fca6ea1SDimitry Andric     exitWithError(
3300*0fca6ea1SDimitry Andric         "--" + NumTestTraces.ArgStr +
3301*0fca6ea1SDimitry Andric         " must be smaller than the total number of traces: expected: < " +
3302*0fca6ea1SDimitry Andric         Twine(Traces.size()) + ", actual: " + Twine(NumTestTraces));
3303*0fca6ea1SDimitry Andric   ArrayRef TestTraces = Traces.take_back(NumTestTraces);
3304*0fca6ea1SDimitry Andric   Traces = Traces.drop_back(NumTestTraces);
3305*0fca6ea1SDimitry Andric 
3306*0fca6ea1SDimitry Andric   std::vector<BPFunctionNode> Nodes;
3307*0fca6ea1SDimitry Andric   TemporalProfTraceTy::createBPFunctionNodes(Traces, Nodes);
330806c3fb27SDimitry Andric   BalancedPartitioningConfig Config;
330906c3fb27SDimitry Andric   BalancedPartitioning BP(Config);
331006c3fb27SDimitry Andric   BP.run(Nodes);
331106c3fb27SDimitry Andric 
33121db9f3b2SDimitry Andric   OS << "# Ordered " << Nodes.size() << " functions\n";
3313*0fca6ea1SDimitry Andric   if (!TestTraces.empty()) {
3314*0fca6ea1SDimitry Andric     // Since we don't know the symbol sizes, we assume 32 functions per page.
3315*0fca6ea1SDimitry Andric     DenseMap<BPFunctionNode::IDT, unsigned> IdToPageNumber;
3316*0fca6ea1SDimitry Andric     for (auto &Node : Nodes)
3317*0fca6ea1SDimitry Andric       IdToPageNumber[Node.Id] = IdToPageNumber.size() / 32;
3318*0fca6ea1SDimitry Andric 
3319*0fca6ea1SDimitry Andric     SmallSet<unsigned, 0> TouchedPages;
3320*0fca6ea1SDimitry Andric     unsigned Area = 0;
3321*0fca6ea1SDimitry Andric     for (auto &Trace : TestTraces) {
3322*0fca6ea1SDimitry Andric       for (auto Id : Trace.FunctionNameRefs) {
3323*0fca6ea1SDimitry Andric         auto It = IdToPageNumber.find(Id);
3324*0fca6ea1SDimitry Andric         if (It == IdToPageNumber.end())
3325*0fca6ea1SDimitry Andric           continue;
3326*0fca6ea1SDimitry Andric         TouchedPages.insert(It->getSecond());
3327*0fca6ea1SDimitry Andric         Area += TouchedPages.size();
3328*0fca6ea1SDimitry Andric       }
3329*0fca6ea1SDimitry Andric       TouchedPages.clear();
3330*0fca6ea1SDimitry Andric     }
3331*0fca6ea1SDimitry Andric     OS << "# Total area under the page fault curve: " << (float)Area << "\n";
3332*0fca6ea1SDimitry Andric   }
33331db9f3b2SDimitry Andric   OS << "# Warning: Mach-O may prefix symbols with \"_\" depending on the "
33341db9f3b2SDimitry Andric         "linkage and this output does not take that into account. Some "
33351db9f3b2SDimitry Andric         "post-processing may be required before passing to the linker via "
33361db9f3b2SDimitry Andric         "-order_file.\n";
333706c3fb27SDimitry Andric   for (auto &N : Nodes) {
33385f757f3fSDimitry Andric     auto [Filename, ParsedFuncName] =
3339*0fca6ea1SDimitry Andric         getParsedIRPGOName(Reader->getSymtab().getFuncOrVarName(N.Id));
33405f757f3fSDimitry Andric     if (!Filename.empty())
334106c3fb27SDimitry Andric       OS << "# " << Filename << "\n";
33425f757f3fSDimitry Andric     OS << ParsedFuncName << "\n";
334306c3fb27SDimitry Andric   }
334406c3fb27SDimitry Andric   return 0;
334506c3fb27SDimitry Andric }
334606c3fb27SDimitry Andric 
llvm_profdata_main(int argc,char ** argvNonConst,const llvm::ToolContext &)334706c3fb27SDimitry Andric int llvm_profdata_main(int argc, char **argvNonConst,
334806c3fb27SDimitry Andric                        const llvm::ToolContext &) {
3349bdd1243dSDimitry Andric   const char **argv = const_cast<const char **>(argvNonConst);
33500b57cec5SDimitry Andric 
33510b57cec5SDimitry Andric   StringRef ProgName(sys::path::filename(argv[0]));
33520b57cec5SDimitry Andric 
33535f757f3fSDimitry Andric   if (argc < 2) {
33545f757f3fSDimitry Andric     errs() << ProgName
33555f757f3fSDimitry Andric            << ": No subcommand specified! Run llvm-profata --help for usage.\n";
33565f757f3fSDimitry Andric     return 1;
33570b57cec5SDimitry Andric   }
33580b57cec5SDimitry Andric 
33595f757f3fSDimitry Andric   cl::ParseCommandLineOptions(argc, argv, "LLVM profile data\n");
33600b57cec5SDimitry Andric 
33615f757f3fSDimitry Andric   if (ShowSubcommand)
3362*0fca6ea1SDimitry Andric     return show_main(ProgName);
336306c3fb27SDimitry Andric 
33645f757f3fSDimitry Andric   if (OrderSubcommand)
3365*0fca6ea1SDimitry Andric     return order_main();
33660b57cec5SDimitry Andric 
33675f757f3fSDimitry Andric   if (OverlapSubcommand)
3368*0fca6ea1SDimitry Andric     return overlap_main();
33690b57cec5SDimitry Andric 
33705f757f3fSDimitry Andric   if (MergeSubcommand)
3371*0fca6ea1SDimitry Andric     return merge_main(ProgName);
33725f757f3fSDimitry Andric 
33735f757f3fSDimitry Andric   errs() << ProgName
33745f757f3fSDimitry Andric          << ": Unknown command. Run llvm-profdata --help for usage.\n";
33750b57cec5SDimitry Andric   return 1;
33760b57cec5SDimitry Andric }
3377